【发布时间】:2014-09-17 07:01:05
【问题描述】:
我们在我们的项目中使用非常优秀的ToStringBuilder 作为我们 ToString 实现的高性能通用支持。在我需要生成对象图的字符串表示以检查它在加载和关闭之间是否发生变化之前,它可以很好地进行调试。以前我使用 MemoryStream 将对象写到 xml,但这似乎很重,所以我决定尝试使用 ToStringBuilder,这是我遇到的阻碍...
我们的对象图大量使用泛型类型列表,因此当列表被打印出来时,它们如下所示:
PropertyName:{System.Collections.Generic.List`1[Namespace.Path.To.MyClassDto]}
而不是枚举列表并在每个对象上调用 ToString,这很好,因为这是默认行为(顺便说一句,ToStringBuilder 支持 object[],但我们不想为了解决这个问题而改造整个 Dto 层) .
我尝试修补有问题的代码(ToStringBuilder.cs,第 177 行)以识别类型何时是通用列表,然后调用 string.Join(", ", list),但我无法获得我的了解 linq 反射 API 如何处理泛型。
我尝试的第一件事是获取 String.Join(IEnumerable) 方法的句柄,如下所示:
var stringJoinMethod = typeof(string).GetMethod("Join", new[] { typeof(string), typeof(IEnumerable<>) });
但是 GetMethod 返回了 null,所以这不起作用。我最终找到了this StackOverflow question,它向我展示了如何通过签名获取泛型方法(改为调用 getmethods() 并过滤结果)。这让我得到了正确的方法句柄,所以我尝试做这样的事情:
private void AppendMember(MemberInfo memberInfo)
{
AppendQuotesIfRequiredForType(memberInfo);
Type type = GetMemberType(memberInfo);
var memberAppendMethod = typeof(StringBuilder).GetMethod("Append", new[] { type });
Expression getMemberValue = Expression.MakeMemberAccess(TargetArgExpression, memberInfo);
if (type.IsValueType)
{
Type appendArgType = memberAppendMethod.GetParameters()[0].ParameterType;
if (type != appendArgType)
{
getMemberValue = Expression.TypeAs(getMemberValue, typeof(object));
}
//my code begins here.
_appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
}
else if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)))
{
// now to emit some code to do the below, you wouldn't think it'd be this hard...
// string.Join(", ", genericList);
AppendStartOfMembers();
//this returns null, because generics are not well supported by the reflection API, boo!
var stringJoinMethod = typeof(string).GetGenericMethod("Join", new[] { typeof(string), typeof(IEnumerable<>) });
var CommaSpace = Expression.Constant(", ");
// this doesn't work, throws an ArgumentException as below
getMemberValue = Expression.Call(stringJoinMethod, CommaSpace, getMemberValue);
_appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
AppendEndOfMembers();
}
else
{
//primitives like strings
_appendExpressions.Add(Expression.Call(SbArgExpression, memberAppendMethod, getMemberValue));
}
//my code ends here.
AppendQuotesIfRequiredForType(memberInfo);
}
出现以下异常的错误:
System.ArgumentException: "Method System.String Join[T](System.String, System.Collections.Generic.IEnumerable`1[T]) is a generic method definition"
at System.Linq.Expressions.Expression.ValidateMethodInfo(MethodInfo method)
at System.Linq.Expressions.Expression.ValidateMethodAndGetParameters(Expression instance, MethodInfo method)
at System.Linq.Expressions.Expression.Call(MethodInfo method, Expression arg0, Expression arg1)
at MyNameSpace.Common.ToStringBuilder`1.AppendMember(MemberInfo memberInfo) in C:\myproject\MyNamespace.Common\ToStringBuilder.cs:line 206
我开始在谷歌上搜索该错误消息,发现人们在谈论使用 Expression.Lamba() 来包装对泛型方法的调用,此时我意识到我已经超出了我的深度。
所以,假设我有一个 List mylist,我如何生成一个如上所述的表达式,它相当于 string.Join(", ", mylist); ?
谢谢!
【问题讨论】:
-
认为您需要
var typedStringJoinMethod =stringJoinMethod.MakeGenericMethod(the generic type of your list)并在该变量上使用 Expression.Call。
标签: linq c#-4.0 generics lambda