如何将一种类型的表达式树转换为不同的表达式类型?
如果我有两个几乎相同的类Animal
和AnimalViewModel
以及一个与viewmodel相关的表达式树,我该如何将它翻译成Animal
?
public class Animal { public string Species { get; set; } public string Name { get; set; } public string Sound { get; set; } } public class AnimalViewModel : ViewModelBase { public string Species { get; set; } public string Name { get; set; } public string Sound { get; set; } }
如何将Expression<Func>
成Expression<Func>
?
public static Expression<Func> Translate (Expression<Func> expression) { // What goes here? I assume I have to traverse the tree somehow. }
这是一个完成这项工作的访客。
- 它创建了一个参数的副本(因为我们需要创建一个新参数并替换旧参数的所有引用)
- 它
.Body
树的.Body
,替换参数,并将对旧类型的任何成员访问切换到新类型的类似命名的成员 - 它使用我们发明的参数重新组装一个lambda
码:
class TypeChangeVisitor : ExpressionVisitor { private readonly Type from, to; private readonly Dictionary substitutions; public TypeChangeVisitor(Type from, Type to, Dictionary substitutions) { this.from = from; this.to = to; this.substitutions = substitutions; } public override Expression Visit(Expression node) { // general substitutions (for example, parameter swaps) Expression found; if(substitutions != null && substitutions.TryGetValue(node, out found)) { return found; } return base.Visit(node); } protected override Expression VisitMember(MemberExpression node) { // if we see x.Name on the old type, substitute for new type if (node.Member.DeclaringType == from) { return Expression.MakeMemberAccess(Visit(node.Expression), to.GetMember(node.Member.Name, node.Member.MemberType, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Single()); } return base.VisitMember(node); } } public class Program { public static void Main() { Expression> predicate = x => x.Name == "abc"; var switched = Translate(predicate); } public static Expression> Translate(Expression> expression) { var param = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name); var subst = new Dictionary { { expression.Parameters[0], param } }; var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst); return Expression.Lambda>(visitor.Visit(expression.Body), param); } }
请注意,如果你有x.Something.Name
你可能需要更加小心,但这应该让你有一个合理的方法。
您是否尝试过Automapper这样的事情?