【问题标题】:How to convert an expression tree?如何转换表达式树?
【发布时间】:2011-02-27 12:04:08
【问题描述】:

更新

我会尽力解释我的意思。 有 2 个不同的类(MyClass1 和 MyClass2)以及将 class1 转换为 class2 的方法:

class MyClass1
  {
        //...Some fields and properties
 }

  class MyClass2
   {
        //...Some fields and properties
  }

public MyClass2 Convert(MyClass1 class1)
{
//.....
return class2Object;
}

有两种不同的方法:

   void method1(Expression<Func<MyClass1, bool>> where, //other parameters)
    {
        //some operations
        //...............

        //need to call method2(Expression<Func<MyClass2, bool>>)
        //   BUT! How do I convert Expression<Func<MyClass1, bool>> 
        //   to Expression<Func<MyClass2, bool>>
    }

    void method2(Expression<Func<MyClass2, bool>> where, //other parameters)
    {
        //some operations
    }

如何将 ExpressionMyClass1, bool>> 转换为 ExpressionMyClass2, bool > >

【问题讨论】:

  • 由于MyClass1MyClass2不能相互转换,你希望如何转换委托?
  • 在您定义 MyClass1MyClass2 之间的显式转换之前,您甚至无法开始。
  • 您的编辑仍然没有说明如何在两者之间进行转换。
  • 如何将 MyClass1 转换为 MyClass2 并不重要。只需使用 ConvertFromClass1ToClass2() 方法即可。
  • 我很少发现任何无关紧要的东西。如果不对您的模型进行扩展思考,这个问题就无法回答,任何提议都可能远离这个问题的范围。请详细说明。

标签: c# .net lambda


【解决方案1】:

让我猜猜你在问什么:你的 MyClass1MyClass2 看起来一样(它们都有一个 int field1 和一个 string field2)。现在你有一个Expression&lt;Func&lt;MyClass1,bool&gt;&gt;,类似于:

Expression<Func<MyClass1, bool>> exp1 = x => x.field1 == 100; // x is MyClass1

你想要另一个表达式,看起来一样,但它是针对MyClass2

Expression<Func<MyClass2, bool>> exp2 = x => x.field1 == 100; // x is MyClass2

如果这是您的要求,这是我的答案:

要获得MyClass2 的表达式,您需要替换exp1 中的所有x,因为exp1 中的所有x 都是MyClass1 类型。 ExpressionVisitor 正是你想要的。

class MyExpressionVisitor : ExpressionVisitor
{
    public ParameterExpression NewParameterExp { get; private set; }

    public MyExpressionVisitor(ParameterExpression newParameterExp)
    {
        NewParameterExp = newParameterExp;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return NewParameterExp;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Member.DeclaringType == typeof(MyClass1))
            return Expression.MakeMemberAccess(this.Visit(node.Expression), 
               typeof(MyClass2).GetMember(node.Member.Name).FirstOrDefault());
        return base.VisitMember(node);
    }
}

访问者将遍历(比如“访问”)整个表达式,访问所有节点。当涉及到ParameterExpression 节点时,我们更改节点(因为是MyClass1,我们将其更改为MyClass2,参见VisitParameter 方法)。我们需要改变的另一件事是,当访问者来到像x.field1 这样的节点时,它正在访问MyClass1 中的field1,我们也需要对其进行修改(参见VisitMember)。经过整个exp1,我们得到一个全新的exp2,替换了一些节点,这就是我们想要的。

Expression<Func<MyClass1, bool>> exp1 = x => x.field1 == 100;

var visitor = new MyExpressionVisitor(Expression.Parameter(typeof(MyClass2), 
                        exp1.Parameters[0].Name));

var exp2 = Expression.Lambda<Func<MyClass2, bool>>
                (visitor.Visit(exp1.Body), visitor.NewParameterExp);

//the following is for testing
var data = new MyClass2();
Console.WriteLine(exp2.Compile()(data));  //False
data.field1 = 100;
Console.WriteLine(exp2.Compile()(data));   //True

【讨论】:

  • 感谢您的回答,但这不是我的意思。
  • @Alex 那么描述一下你的意思怎么样?
  • @Alex:你能举个Expression的例子吗?
  • 完美,这正是我所需要的!非常适合我。
【解决方案2】:

表达式树是不可变的,所以要做到这一点,你需要遍历整个树,重建它并用等效的类型替换任何使用类型 - 通常通过编写“访问者”。当遇到 MemberExpression 或 MethodCallExpression 时,您会检查成员的声明类型 - 如果它是您不想要的类型,请重新创建它(Expression.PropertyOrField 在这里很有用)。

请注意,您不能只在使用它的地方这样做;必须重新生成整棵树。我现在不在电脑前,但如果你愿意,我可以稍后再做一个例子;如果您需要此示例,请发表评论。

请注意,由于 int/long 和 char/string 不匹配,这有点复杂。

【讨论】:

  • 是的,请。举个例子吧。
【解决方案3】:
public CategoryViewModel GetSingle( Expression<Func<CategoryViewModel, bool>> where)
        {
            Expression<Func<DAL.EntityModels.Category, CategoryViewModel>> converter =
                c => ToBll(c);

            var param = Expression.Parameter(typeof(DAL.EntityModels.Category), "category");
            var body = Expression.Invoke(where, Expression.Invoke(converter, param));
            var lambda = Expression.Lambda<Func<DAL.EntityModels.Category, bool>>(body, param);

            return  (CategoryViewModel )_categoryRepository.GetSingle(lambda);

        }

//..............
public T GetSingle(Expression<Func<T, bool>> where)
        {
            return this.ObjectSet.Where(where).FirstOrDefault<T>();
        }

【讨论】:

    【解决方案4】:

    您可以将第一个表达式编译为委托,然后将其转换为
    NJection.LambdaConverter,这是一个将委托转换为表达式树的库。

    【讨论】:

      猜你喜欢
      • 2011-04-09
      • 1970-01-01
      • 2012-01-15
      • 1970-01-01
      • 2013-08-15
      • 1970-01-01
      • 2014-02-04
      • 2010-11-21
      相关资源
      最近更新 更多