Fork me on GitHub
代码改变世界

如何执行表达式树

2018-01-10 09:42  沉睡的木木夕  阅读(597)  评论(1编辑  收藏  举报

执行表达式树

本节主要展示如何去执行表达式树。运行一个可能含有返回值或只是执行一个操作,比如方法调用的表达式树。

只有表示lambda表达式的表达式树能够被执行。它是一个 LambdaExpressionExpression 类型。为了执行这些表达式树,调用 Compile 方法来生成一个可执行的委托并调用它。

注意:

如果这个委托的类型是未知的,那么这个委托的类型是LambdaExpression而不是Expression,你必须调用委托的 DynamicInvoke 方法而不是直接调用Invoke。

如果一个表达式树不代表一个lambda表达式,你可以创建一个新的表达式树将原来的表达式树来作为它的Body,通过调用 Lambda(Expression, IEnumerable) 方法。然后你就可以调用这个lambda表达式了

Example

下面的代码说明如何运行一个表示一个数的幂运算的表达式树通过生成lambda并调用它。结果是显示这个数的平方

//执行表达式树
BinaryExpression be = Expression.Power(Expression.Constant(2D), Expression.Constant(3D));
//创建一个委托表达式
Expression<Func<double>> le = Expression.Lambda<Func<double>>(be);
// 编译lambda表达式
Func<double> compiledExpression = le.Compile();
//执行lambda表达式
double result = compiledExpression();
//显示值
Console.WriteLine(result);

编译的代码

  • 添加项目引用 System.Core.dll
  • 添加命名空间 System.Linq.Expressions

如何修改表达式树

这节主要展示怎样去修改表达式树。表达式树是不可变的(Immutable),意味着它不能被直接修改。为了修改表达式树,那么你必须新建一个已经存在的表达式树的副本,并在创建副本时进行所需的更改。你可以使用 ExpressionVisitor 类去解析表达式树并复制它访问的每一个节点。

修改表达式树

  1. 新建控制台应用程序

  2. 添加引用 System.Linq.Expressions

  3. 在你的项目中添加类 AndAlsoModifier

    public class AndAlsoModifier : ExpressionVisitor
    {
        public Expression Modify(Expression expression)
        {
            return Visit(expression);
        }
    
        protected override Expression VisitBinary(BinaryExpression b)
        {
            if(b.NodeType == ExpressionType.AndAlso)
            {
                Expression left = this.Visit(b.Left);
                Expression right = this.Visit(b.Right);
    
                //让二元运算符OrElse代替AndAlso
                return Expression.MakeBinary(ExpressionType.OrElse, left, right, b.IsLiftedToNull, b.Method);
            }
            return base.VisitBinary(b);
        }
    }
    

    这个类继承了 ExpressionVisitor 而且专门用来修改表示条件 And 操作的表达式。它改变从 And 条件到 OR。为了这个目的, AndAlsoModifier 重写了基类的 VisitBinary 方法,因为 And 表示的是一个二元表达式。在 VisitBinary 方法中,如果这个表达式传递的是 And 操作,代码会构造一个包含条件操作 OR 新的表达式而不是 And。如果表达式传给 VisitBinary 的不是 And 操作,那么方法就会优先基类的实现。它基类的方法构造一个节点就像传递进来的表达式树一样,但是这个节点有它们的子树,被访问者递归生成的表达式树替换。

  4. 添加引用 System.Linq.Expressions

  5. 在 Program.cs 文件添加 Main 方法并并创建一个表达式树传递给这个方法来修改它。

    Expression<Func<string, bool>> expr = name => name.Length > 10 && name.StartsWith("G");  
    Console.WriteLine(expr);  
    
    AndAlsoModifier treeModifier = new AndAlsoModifier();  
    Expression modifiedExpr = treeModifier.Modify((Expression) expr);  
    
    Console.WriteLine(modifiedExpr);  
    
    /*  This code produces the following output:  
    
        name => ((name.Length > 10) && name.StartsWith("G"))  
        name => ((name.Length > 10) || name.StartsWith("G"))  
    */  
    

    这段代码创建了一个包含 And 操作的表达式树。然后新建一个 AndAlsoModifier 的实例并给方法 Modify 传递之前创建的表达式树。并输出原始和修改后的表达式树显示差异。

  6. 编译并运行程序。