我有一个记录器,它抽象了对NLog的调用。我希望每个日志行都有它的调用者来源。一些带有泛型的代码会导致奇怪的调用方名称:
代理服务`1[MyNameSpace.IController]
在IDE中,当我将鼠标悬停在变量调用程序上时,它会弹出正确的名称:
{MyNameSpace.ProxyService}
如何获得与IDE弹出的值相同的值?
代码样本:
private void SetLogEventInfo(object caller, ILogger logger)
{
string callerOrigin = null;
if (caller != null)
{
callerOrigin = caller.ToString();
}
<code removed>
}
您可以通过连接来获得类似于IDE的结果
Type type = value.GetType();
return type.Namespace + "." + type.Name;
通过这种方式可以获得MyNameSpace.ProxyService`1
。`1
位指的是泛型类型中类型参数的数量。没有它,这个名字就是ambigus。如果您坚持要删除它,可以使用常用的字符串操作(IndexOf
、Remove
)。
首先,我们的IDE以C#表示法向您显示类名,System.Reflection
与语言无关,因此存在差异。
其次,不要依赖ToString()
。只有在未重写的情况下,它才会显示类型名称(因此使用默认的Object
实现)。在多个有效的场景中,您希望覆盖ToString()
,这会破坏您的代码。
模拟C#代码格式是AFAIK不可用的开箱即用,但有一些技巧可以做到这一点,例如:
var cSharpProvider = CodeDomProvider.CreateProvider("C#");
var variableDecl = new CodeVariableDeclarationStatement(caller.GetType(), "_");
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
cSharpProvider.GenerateCodeFromStatement(variableDecl,
sw,
new CodeGeneratorOptions());
}
sb.Replace("_;", String.Empty);
var callerOrigin = sb.ToString().Trim();
正在调用对象的ToString
方法,如果不重写该方法,它将简单地返回类型名称,相当于:
public string ToString() {
return this.GetType().ToString();
}
如果您愿意,可以重写ToString,但这显然不适用于您无法控制的类型。例如,如果List<string>
的一个实例被传递到该方法中,那么您最终会得到:
System.Collections.Generic.List`1[System.String]
这可能对你没有好处。因此,最好的办法是修改上面的代码以格式化类型:
callerOrigin = FormatObjectForLog(caller);
该方法可能会做一些类似的事情(你可以填空,因为我对你的需求了解不够,无法决定如何处理ProxyService`1以外的情况):
string FormatObjectForLog(object obj) {
if (obj is IFormattable)
return obj.ToString();
if (obj.GetType().IsGenericType) {
// Get rid of `1 and other crud...
}
...
}
一个版本的净化类型格式化程序可以消除反勾号,如下所示:
public string FormatType(Type t) {
if (t.IsGenericType) {
return string.Format(
"{0}.{1}<{2}>",
t.Namespace,
t.Name.Substring(0, t.Name.IndexOf('`')),
string.Join(", ", t.GetGenericArguments().Select(FormatType))
);
}
return t.ToString();
}