如何转换表达式树?

发布于 2024-10-19 13:48:28 字数 1022 浏览 2 评论 0 原文

更新

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

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
    }

如何转换 Expression<功能< MyClass1,布尔>>>到表达式<功能< MyClass2,布尔> >

UPDATE:

I'll try to explain what I mean.
There are 2 different classes (MyClass1 and MyClass2) and method to convert class1 to class2:

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

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

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

There are 2 different methods:

   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
    }

How do I convert Expression< Func< MyClass1, bool>> to Expression< Func< MyClass2, bool > >

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

北恋 2024-10-26 13:48:28

让我猜猜您在问什么:您的 MyClass1MyClass2 看起来相同(它们都有一个 int field1 和一个 string field2)。现在您有一个 Expression>,类似于:

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

Let me guess what you are asking: Your MyClass1 and MyClass2 look the same (they both have an int field1 and a string field2). Now you have an Expression<Func<MyClass1,bool>>, something like:

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

And you want another expression, which looks the same, but it's for MyClass2:

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

If this is what you are asking, here is my answer:

To get the expression for MyClass2, you need to replace all x in exp1, because all x in exp1 are of type MyClass1. ExpressionVisitor is exactly what you want.

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

The visitor will go through(say "visit") the whole expression, visit all the nodes. When it comes to an ParameterExpression node, we change the node (because it's MyClass1, we change it to MyClass2, see VisitParameter method). Another thing we need to change is, when the visitor comes to a node like x.field1, it's visiting the field1 in MyClass1, we need to modify it too(see VisitMember). After going through the whole exp1, we get a totally new exp2, with some nodes replaced, that's what we want.

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
平定天下 2024-10-26 13:48:28

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

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

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

Expression trees are immutable, so to do this ou would need to walk the entire tree, rebuilding it and substiting any uses of the type with the equivalent - usually by writing a "visitor". When encountering a MemberExpression or MethodCallExpression, you would check the member's declaring type - if it is the one ou don't want, recreate it (Expression.PropertyOrField is useful here).

Note that you can't do this just at the places it is used; the entire tree must be regenerated. I'm not at a PC at the moment, but if you want I can do an example later; leave a comment if you need this example.

Note that this is somewhat complicated by the int/long and char/string mismatch.

不知所踪 2024-10-26 13:48:28
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>();
        }
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>();
        }
无可置疑 2024-10-26 13:48:28

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

You could compile the first expression to a delegate and then convert it with
NJection.LambdaConverter which is a library that converts delegates to expression trees.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文