2011-02-27 61 views
1

UPDATE如何转换表达式树?

我会尽力解释我的意思。 有2 不同类(MyClass1的和MyClass2)和方法的Class1转换为等级2:

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

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

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

有2点不同的方法:

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 
    } 

如何转换表达< Func键< MyClass1,bool >>至表达式< Func < MyClass2,bool >>

+1

既然'MyClass1'和'MyClass2'不能互相转换,你怎么期望转换委托? – 2011-02-27 12:08:43

+1

直到你定义'MyClass1'和'MyClass2'之间的显式转换,你甚至不能开始。 – 2011-02-27 12:09:16

+0

看看我的编辑。 – Alexandre 2011-02-27 12:14:32

回答

1

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

请注意,您不能仅在使用它的地方执行此操作;整棵树必须重新生成。我目前不在PC上,但如果你想要,我可以稍后做一个例子;如果你需要这个例子,请留下评论。

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

+0

是的,请。给我举个例子。 – Alexandre 2011-02-27 20:29:20

6

让我猜猜你问:你MyClass1MyClass2看起来一样(它们都具有一个int FIELD1和一个字符串域2)。现在你有一个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,你需要替换exp1所有x,因为所有exp1中的是MyClass1ExpressionVisitor正是你想要的。

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

访问者将通过(称为“访问”)整个表达式,访问所有节点。当涉及到一个​​节点时,我们更改节点(因为它是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 
+0

感谢您的回答,但这不是我的意思。 – Alexandre 2011-02-27 18:10:39

+2

@Alex那么如何描述你的意思? – CodesInChaos 2011-02-27 18:14:27

+0

我编辑了我的问题。 – Alexandre 2011-02-27 18:17:05

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