表达式树,说说表达式树

C# 知识回顾 - 表达式树 Expression Trees

 

[C#] 说说表达式树,

表达式树(Expression Trees)

2018-01-08 19:00 by 沉睡的木木夕, 69 阅读, 1 评论, 收藏, 编辑

目录

  • 简介
  • Lambda 表达式创建表达式树
  • API 创建表达式树
  • 解析表达式树
  • 表达式树的永久性
  • 编译表达式树
  • 执行表达式树
  • 修改表达式树
  • 调试

 

说说表达式树 - Expression Trees

[翻译]表达式树(Expression Trees)

*原文地址:

表达式树展示的是代码的树形结构,每一个节点都是一个表达式,例如:调用一个方法或是调用一个二元运算表达式比如 x < y

你可以通过表达式树来编译和运行代码。这意味着能动态修改运行的代码,就好比在数据库中运行LINQ以一个变量的形式查询数据和创建一个动态的查询语句。关于表达式树在LINQ的更多使用信息详见How to: Use Expression Trees to Build Dynamic Queries (C#).

表达式树也被用于DLR(动态语言运行时),提供DLR与.NET Framework之间的互操作性,使编译器解析(Emit)表达式树,而不是MSIL。关于DLR更多信息详细详见Dynamic Language Runtime Overview.

你可以用C#/VB编译器在你的lambda表达式变量的基础上生成一个表达式树,或者你可以通过使用在System.Linq.Expressions命名空间创建表达式树.

简介

  表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等。

  你可以对表达式树中的代码进行编辑和运算。这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。 

  表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性。 

 

  简单说下表达式树。

 

从Lambda表达式创建表达式树

当一个lambda表达式被分配到类型为Expression的的一个变量时,编译器会解析生成代码创建一个表达式树来表示这个lambda .

C#编译器能从lambda生成表达式树(或者从一个单行的lambda)。但它不能转换成lambda声明(或多行lambda)。更多关于lambda信息见Lambda Expressions.

下面的代码例子说明了如何用C#来生成一个表达式树来表示一个lambda表达式: num => num < 5

Expression<Func<int, bool>> lambda = num => num < 5;

一、Lambda 表达式创建表达式树

  若 lambda 表达式被分配给 Expression<TDelegate> 类型的变量,则编译器可以发射代码以创建表示该 lambda 表达式的表达式树。  

  C# 编译器只能从表达式 lambda (或单行 lambda)生成表达式树。 

  下列代码示例使用关键字 Expression创建表示 lambda 表达式:

1             Expression<Action<int>> actionExpression = n => Console.WriteLine(n);
2             Expression<Func<int, bool>> funcExpression1 = (n) => n < 0;
3             Expression<Func<int, int, bool>> funcExpression2 = (n, m) => n - m == 0;

 

目录

 

通过API创建表达式树

使用微软提供的API——Expression这个类来创建表达式树。这个类包括了创建指定类型表达式树节点的静态工厂方法,例如, ParameterExpression它代表一个参数或者变量,又如MethodCallExpression,它代表一个方法调用。ParameterExpression, MethodCallExpression以及其他的指定类型的表达式树都在System.Linq.Expressions命名空间下。这些类都继承自 Expression抽象类.

下面的代码展示如何用API创建表达式树来表示一个lambda表达式num => num < 5

// 在你的代码文件添加引用:  
// using System.Linq.Expressions;  

// 为lambda表达式num => num < 5手动生成表达式树   
ParameterExpression numParam = Expression.Parameter(typeof(int), "num");  
ConstantExpression five = Expression.Constant(5, typeof(int));  
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);  
Expression<Func<int, bool>> lambda1 =  
    Expression.Lambda<Func<int, bool>>(  
        numLessThanFive,  
        new ParameterExpression[] { numParam }); 

在.NET Framework4.0之后,表达式树API也支持分配和控制流表达式,例如循环,条件判断块以及异常捕捉块(try-catch)。通过API,你可以创建比编译器通过从lambda表达式创建的更加复杂的表达式树。下面的这个例子展示了如何创建一个表达式树来表示一个数的阶乘(factorial of number).

//创建一个参数表达式
ParameterExpression value = Expression.Parameter(typeof(int), "value");
//创建一个表达式表示本地变量
ParameterExpression result = Expression.Parameter(typeof(int), "result");
//创建标签从循环跳到指定标签
LabelTarget label = Expression.Label(typeof(int));
//创建方法体
BlockExpression block = Expression.Block(
    //添加本地变量
    new[] { result },
    //为本地变量赋值一个常量
    Expression.Assign(result, Expression.Constant(1)),
    //循环
    Expression.Loop(
        //添加循环条件
        Expression.IfThenElse(
            //条件:value > 1
            Expression.GreaterThan(value, Expression.Constant(1)),
            //if true
            Expression.MultiplyAssign(result, Expression.PostDecrementAssign(value)),
            //if false
            Expression.Break(label, result)
            ),
        label
        )
    );

int facotorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);  
Console.WriteLine(factorial);  
// 输出 120.  

更多关于详见Generating Dynamic Methods with Expression Trees in Visual Studio 2010,也支持后续VS版本.

二、API 创建表达式树

  通过 API 创建表达式树需要使用 Expression 类

  下列代码示例展示如何通过 API 创建表示 lambda 表达式:num => num == 0

1             //通过 Expression 类创建表达式树
2             //  lambda:num => num == 0
3             ParameterExpression pExpression = Expression.Parameter(typeof(int));    //参数:num
4             ConstantExpression cExpression = Expression.Constant(0);    //常量:0
5             BinaryExpression bExpression = Expression.MakeBinary(ExpressionType.Equal, pExpression, cExpression);   //表达式:num == 0
6             Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(bExpression, pExpression);  //lambda 表达式:num => num == 0

  代码使用 Expression 类的静态方法进行创建。

 

介绍

  表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等。

  你可以对表达式树中的代码进行编辑和运算。这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。 

  表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL)。 

 

解析表达式树(Parsing Expression Trees)

下面的代码示例演示了如何将表达式树表示为lambda表达式num => num < 5,可以被分解为部分。

public void DecomposedExpressionTrees()
{
    //创建一个表达式树
    Expression<Func<int, bool>> exprTree = num => num < 5;
    //分解表达式
    ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];
    //num < 5
    BinaryExpression operation = (BinaryExpression)exprTree.Body;
    ParameterExpression left = (ParameterExpression)operation.Left;
    ConstantExpression right = (ConstantExpression)operation.Right;

    Console.WriteLine("Decomposed expression: {0} => {1} {2} {3}",
            param.Name, left.Name, operation.NodeType, right.Value);
}

三、解析表达式树 

   下列代码示例展示如何分解表示 lambda 表达式 num => num == 0 的表达式树。

1             Expression<Func<int, bool>> funcExpression = num => num == 0;
2 
3             //开始解析
4             ParameterExpression pExpression = funcExpression.Parameters[0]; //lambda 表达式参数
5             BinaryExpression body = (BinaryExpression)funcExpression.Body;  //lambda 表达式主体:num == 0
6 
7             Console.WriteLine($"解析:{pExpression.Name} => {body.Left} {body.NodeType} {body.Right}");

图片 1

 

 

 

一、根据 Lambda 表达式创建表达式树

  若 lambda 表达式被分配给 Expression<TDelegate> 类型的变量,则编译器可以发射代码以创建表示该 lambda 表达式的表达式树。  

  C# 编译器只能从表达式 lambda (或单行 lambda)生成表达式树。 

  下列代码示例使用关键字 Expression创建表示 lambda 表达式:

1             Expression<Action<int>> actionExpression = n => Console.WriteLine(n);
2             Expression<Func<int, bool>> funcExpression1 = (n) => n < 0;
3             Expression<Func<int, int, bool>> funcExpression2 = (n, m) => n - m == 0;

 

表达式树的不变性(Immutability of Expression Trees)

表达式树应该是不可变的。这意味着如果你想修改表达式树,那么你必须重新已经存在的构造表达式树并替换其某个节点。你可以使用表达式树访问器(ExpressionVisitor)遍历表达式树。更多这方面信息详见How to: Modify Expression Trees (C#).

四、表达式树永久性

  表达式树应具有永久性(类似字符串)。这意味着如果你想修改某个表达式树,则必须复制该表达式树然后替换其中的节点来创建一个新的表达式树。  你可以使用表达式树访问者遍历现有表达式树。第七节介绍了如何修改表达式树。

 

二、通过 API 创建表达式树

  通过 API 创建表达式树需要使用 Expression 类

  下列代码示例展示如何通过 API 创建表示 lambda 表达式:num => num == 0

1             //通过 Expression 类创建表达式树
2             //  lambda:num => num == 0
3             ParameterExpression pExpression = Expression.Parameter(typeof(int));    //参数:num
4             ConstantExpression cExpression = Expression.Constant(0);    //常量:0
5             BinaryExpression bExpression = Expression.MakeBinary(ExpressionType.Equal, pExpression, cExpression);   //二元表达式:num == 0
6             Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(bExpression, pExpression);  //lambda 表达式:num => num == 0

  代码使用 Expression 类的静态方法进行创建。

 

编译表达式树

泛型 Expression 类型提供一个 Compile方法来将表达式树所表示的代码编译成可执行的委托。

下面这段代码显示如何编译表达式树和运行结果代码

public void ComplieExpressTrees()
{
    //创建一个表达式树
    Expression<Func<int, bool>> expr = num => num < 5;
    //编译表达式树为委托
    Func<int, bool> result = expr.Compile();
    //调用委托并写结果到控制台
    Console.WriteLine(result(4));

    //也可以使用简单的语法来编译运行表达式树
    Console.WriteLine(expr.Compile()(4));
    //结果一样
}

更多关于如何运行表达式树信息,详见How to: Execute Expression Trees (C#).

希望有个生活精彩的程序人生

 

 

 

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

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

注意:

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

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

五、编译表达式树

  Expression<TDelegate> 类型提供了 Compile 方法以将表达式树表示的代码编译成可执行委托。

1             //创建表达式树
2             Expression<Func<string, int>> funcExpression = msg => msg.Length;
3             //表达式树编译成委托
4             var lambda = funcExpression.Compile();
5             //调用委托
6             Console.WriteLine(lambda("Hello, World!"));
7 
8             //语法简化
9             Console.WriteLine(funcExpression.Compile()("Hello, World!"));

图片 2

 

 

 

 

 三、解析表达式树 

   下列代码示例展示如何分解表示 lambda 表达式 num => num == 0 的表达式树。

1             Expression<Func<int, bool>> funcExpression = num => num == 0;
2 
3             //开始解析
4             ParameterExpression pExpression = funcExpression.Parameters[0]; //lambda 表达式参数
5             BinaryExpression body = (BinaryExpression)funcExpression.Body;  //lambda 表达式主体:num == 0
6 
7             Console.WriteLine($"解析:{pExpression.Name} => {body.Left} {body.NodeType} {body.Right}");

1 //创建表达式树 2 Expression<Func<string, int>> funcExpression = msg => msg.Length; 3 //表达式树编译成委托 4 var lambda = funcExpression.Compile(); 5 //调用委托 6 Console.WriteLine(lambda("Hello, World!")); 7 8 //语法简化 9 Console.WriteLine(funcExpression.Compile()("Hello, World!"));

1 const int n = 1; 2 const int m = 2; 3 4 //待执行的表达式树 5 BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m)); 6 //创建 lambda 表达式 7 Expression<Func<int>> funcExpression = Expression.Lambda<Func<int>>(bExpression); 8 //编译 lambda 表达式 9 Func<int> func = funcExpression.Compile(); 10 11 //执行 lambda 表达式 12 Console.WriteLine($"{n} {m} = {func()}");

图片 3

 

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

六、执行表达式树

  执行表达式树可能会返回一个值,也可能仅执行一个操作(例如调用方法)。

  只能执行表示 lambda 表达式的表达式树。表示 lambda 表达式的表达式树属于 LambdaExpression 或 Expression<TDelegate> 类型。若要执行这些表达式树,需要调用 Compile 方法来创建一个可执行委托,然后调用该委托。

 1             const int n = 1;
 2             const int m = 2;
 3 
 4             //待执行的表达式树
 5             BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m));
 6             //创建 lambda 表达式
 7             Expression<Func<int>> funcExpression = Expression.Lambda<Func<int>>(bExpression);
 8             //编译 lambda 表达式
 9             Func<int> func = funcExpression.Compile();
10 
11             //执行 lambda 表达式
12             Console.WriteLine($"{n}   {m} = {func()}");

图片 4

 

七、如何:修改表达式树 

   该类继承 ExpressionVisitor 类,并且专用于修改表示条件 AND 运算的表达式。它将这些运算从条件 AND 更改为条件 OR。为此,该类将重写基类型的 VisitBinary 方法,这是因为条件 AND 表达式表示为二元表达式。在 VisitBinary 方法中,如果传递到该方法的表达式表示条件AND 运算,代码将构造一个包含条件 OR 运算符(而不是条件 AND 运算符)的新表达式。如果传递到 VisitBinary 的表达式不表示条件 AND运算,则该方法交由基类实现来处理。  基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。  

 1     internal class Program
 2     {
 3         private static void Main(string[] args)
 4         {
 5             Expression<Func<int, bool>> funcExpression = num => num == 0;
 6             Console.WriteLine($"Source: {funcExpression}");
 7 
 8             var visitor = new NotEqualExpressionVisitor();
 9             var expression = visitor.Visit(funcExpression);
10 
11             Console.WriteLine($"Modify: {expression}");
12 
13             Console.Read();
14         }
15 
16         /// <summary>
17         /// 不等表达式树访问器
18         /// </summary>
19         public class NotEqualExpressionVisitor : ExpressionVisitor
20         {
21             public Expression Visit(BinaryExpression node)
22             {
23                 return VisitBinary(node);
24             }
25 
26             protected override Expression VisitBinary(BinaryExpression node)
27             {
28                 return node.NodeType == ExpressionType.Equal
29                     ? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) //重新弄个表达式:用 != 代替 ==
30                     : base.VisitBinary(node);
31             }
32         }
33     }

图片 5

 

 


【原文链接】 

 

预览版,更新于09/16

 

  

] 说说表达式树, 说说表达式树 - Expression Trees 序 简单说下表达式树。 目录 介绍 表达式树以树形数据结构表示代码,其中每一个节点都...

编译的代码

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

七、修改表达式树 

   该类继承 ExpressionVisitor 类,通过 Visit 方法间接调用 VisitBinary 方法将 != 替换成 ==。基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。  

 1     internal class Program
 2     {
 3         private static void Main(string[] args)
 4         {
 5             Expression<Func<int, bool>> funcExpression = num => num == 0;
 6             Console.WriteLine($"Source: {funcExpression}");
 7 
 8             var visitor = new NotEqualExpressionVisitor();
 9             var expression = visitor.Visit(funcExpression);
10 
11             Console.WriteLine($"Modify: {expression}");
12 
13             Console.Read();
14         }
15 
16         /// <summary>
17         /// 不等表达式树访问器
18         /// </summary>
19         public class NotEqualExpressionVisitor : ExpressionVisitor
20         {
21             public Expression Visit(BinaryExpression node)
22             {
23                 return VisitBinary(node);
24             }
25 
26             protected override Expression VisitBinary(BinaryExpression node)
27             {
28                 return node.NodeType == ExpressionType.Equal
29                     ? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) //重新弄个表达式:用 != 代替 ==
30                     : base.VisitBinary(node);
31             }
32         }
33     }

图片 6

 

 

 

如何修改表达式树

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

八、调试

  8.1 参数表达式

1             ParameterExpression pExpression1 = Expression.Parameter(typeof(string));
2             ParameterExpression pExpression2 = Expression.Parameter(typeof(string), "msg");

图片 7

图8-1

图片 8

图8-2

   从 DebugView 可知,如果参数没有名称,则会为其分配一个自动生成的名称。

 

1             const int num1 = 250;
2             const float num2 = 250;
3 
4             ConstantExpression cExpression1 = Expression.Constant(num1);
5             ConstantExpression cExpression2 = Expression.Constant(num2);

图片 9

图8-3

图片 10

图8-4

   从 DebugView 可知,float 比 int 多了个后缀 F。

 

1             Expression lambda1 = Expression.Lambda<Func<int>>(Expression.Constant(250));
2             Expression lambda2 = Expression.Lambda<Func<int>>(Expression.Constant(250), "CustomName", null);

图片 11

图8-5

图片 12

图8-6

   观察 DebugView ,如果 lambda 表达式没有名称,则会为其分配一个自动生成的名称。

 

修改表达式树

  1. 新建控制台应用程序

  2. 添加引用 System.Linq.Expressions

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

```c#
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 操作,那么方法就会优先基类的实现。它基类的方法构造一个节点就像传递进来的表达式树一样,但是这个节点有它们的子树,被访问者递归生成的表达式树替换。

  1. 添加引用 System.Linq.Expressions

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

```c#
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 传递之前创建的表达式树。并输出原始和修改后的表达式树显示差异。

  1. 编译并运行程序。

希望有个生活精彩的程序人生

 

表达式树(Expression Trees)

 

 

 

 


【原文链接】 

【参考】微软官方文档

 

 

本文由星彩网app下载发布于计算机编程,转载请注明出处:表达式树,说说表达式树

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。