我们正在使用非常优秀的字符串生成器在我们的项目中作为 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,因此不起作用。我最终发现这个 StackOverflow 问题这向我展示了如何通过签名获取通用方法(改为调用 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); 的操作?
thanks!