【问题标题】:How do I translate an expression tree of one type to a different expression type?如何将一种类型的表达式树转换为不同的表达式类型?
【发布时间】:2011-07-14 19:02:18
【问题描述】:

如果我有两个几乎相同的类AnimalAnimalViewModel 以及一个与视图模型相关的表达式树,我如何将其转换为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<AnimalViewModel,bool>> 翻译成Expression<Func<Animal,bool>>

public static Expression<Func<Animal,bool>> Translate (Expression<Func<AnimalViewModel,bool>> expression)
{
  // What goes here?  I assume I have to traverse the tree somehow.
}

【问题讨论】:

    标签: c# c#-4.0 lambda expression-trees


    【解决方案1】:

    这是一个完成这项工作的访客。

    • 它会复制参数(因为我们需要创建一个新参数并将旧参数的所有引用替换为新参数)
    • 它遍历树的 .Body,替换参数,并将针对 old 类型的任何成员访问切换为 new 上的同名成员输入
    • 它使用我们之前发明的参数重新组装一个 lambda

    代码:

    class TypeChangeVisitor : ExpressionVisitor
    {
        private readonly Type from, to;
        private readonly Dictionary<Expression, Expression> substitutions;
        public TypeChangeVisitor(Type from, Type to, Dictionary<Expression, Expression> 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<Func<AnimalViewModel, bool>> predicate = x => x.Name == "abc";
            var switched = Translate<AnimalViewModel, Animal>(predicate);
        }
        public static Expression<Func<TTo, bool>> Translate<TFrom, TTo>(Expression<Func<TFrom, bool>> expression)
        {
            var param = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name);
            var subst = new Dictionary<Expression, Expression> { { expression.Parameters[0], param } };
            var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst);
            return Expression.Lambda<Func<TTo, bool>>(visitor.Visit(expression.Body), param);
        }
    }
    

    请注意,如果您有 x.Something.Name,您可能需要更加小心,但这应该会为您提供合理的方法。

    【讨论】:

    • 如果从具有公共属性的抽象类派生AnimalViewModel 为什么它不起作用?尽管如果您将相同的属性放入 AnimalViewModel 而不是抽象类中,它会起作用
    • @Agzam 在这种情况下将 DeclaringType 替换为 ReflectedType
    • 自己检查一下——如果你在 Animal 中有 Int Id 并且还有从 BaseViewModel 派生的 AnimalViewModel,它持有 Id。如果您尝试在谓词中使用 Id 将不起作用。不知道为什么
    【解决方案2】:

    你有没有试过Automapper这样的东西?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-06
      相关资源
      最近更新 更多