如何转换表达式树?

时间:2011-02-27 12:04:08

标签: c# .net lambda

更新

我会试着解释一下我的意思。 有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
    }

如何转换Expression&lt; FUNC&LT; MyClass1 ,bool&gt;&gt;表达&lt; FUNC&LT; MyClass2 ,bool&gt; &GT;

4 个答案:

答案 0 :(得分:6)

让我猜猜你在问:你的MyClass1MyClass2 看起来是一样的(它们都有一个int field1和一个字符串field2)。现在你有一个Expression<Func<MyClass1,bool>>,类似于:

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的表达式,您需要替换x中的所有exp1,因为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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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>();
        }

答案 3 :(得分:0)

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