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

目录

简介

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

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

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

一、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 类

  下列代码示例展示如何通过 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 类的静态方法进行创建。

三、解析表达式树

  下列代码示例展示如何分解表示 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}");

四、表达式树永久性

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

五、编译表达式树

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

六、执行表达式树

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

  只能执行表示 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()}");

七、修改表达式树

  该类继承 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 }

八、调试

  8.1 参数表达式

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

图8-1

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

图8-3

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

图8-5

图8-6

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

C# 知识回顾 - 表达式树 Expression Trees的更多相关文章

  1. [C#] C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  2. 表达式树(Expression Tree)

    饮水思源 本文并非原创而是下面网址的一个学习笔记 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/e ...

  3. 表达式树 Expression

    转载泛型方法动态生成表达式树 Expression public string GetGridJSON(TraderInfo model) { IQueryable<TraderInfo> ...

  4. C# 表达式树 Expression

    表达式树是定义代码的数据结构. 它们基于编译器用于分析代码和生成已编译输出的相同结构. 几种常见的表达式 BinaryExpression 包含二元运算符的表达式 BinaryExpression b ...

  5. 泛型方法动态生成表达式树 Expression

    public string GetGridJSON(TraderInfo model) { IQueryable<TraderInfo> Temp = db.TraderInfo; if ...

  6. jQuery find() 搜索所有段落中的后代 C# find() 第一个匹配元素 Func 有返回值 Action是没有返回值 Predicate 只有一个参数且返回值为bool 表达式树Expression

    所有p后代span Id为 TotalProject 的 select 标签 的后代 option标签 为选中的 text using System; using System.Collections ...

  7. 利用表达式树Expression优化反射性能

    最近做了一个.Net Core环境下,基于NPOI的Excel导入导出以及Word操作的服务封装,涉及到大量反射操作,在性能优化过程中使用到了表达式树,记录一下. Excel导入是相对比较麻烦的一块, ...

  8. 表达式树Expression

    Expression表达式树动态查询 在进行数据列表的查询中,我们通常会使用两种方式进行查询: linq查询 数据库sql语句查询 这样固然可以实现查询,本人之前也都是这么做的,因为查询的条件很少.使 ...

  9. Func委托与表达式树Expression

    最近在写ORM框架,其中遇到一个难点,就是作为框架调用方如何将查询条件传入框架内.其中就用到了Expression. Func委托 要Expression先要了解Func委托,Func委托的样式是: ...

随机推荐

  1. 『Linux基础 - 5 』Linux常用命令(2)

    这篇笔记的只要知识点: (1)ls查看文件信息,列表中每个字符所代表的含义 (2) 使用通配符匹配文件 (3) chmod命令:修改文件或目录权限 (4) 与用户相关命令(who.su.exit.pa ...

  2. PAT 1001 害死人不偿命的(3n+1)猜想

    1001 害死人不偿命的(3n+1)猜想 (15 分) 卡拉兹(Callatz)猜想: 对任何一个正整数 n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把 (3n+1) 砍掉一半.这样一直反复 ...

  3. Leecode刷题之旅-C语言/python-204计数质数

    /* * @lc app=leetcode.cn id=204 lang=c * * [204] 计数质数 * * https://leetcode-cn.com/problems/count-pri ...

  4. java 代码块的执行顺序

    举一个实例程序: class HelloA { public HelloA(){ System.out.println("Hello A!父类构造方法"); } { System. ...

  5. linux线程篇 (一) 线程的基本概念

    --进程 一个正在执行的程序,资源分配的最小单位 进程中的事情需要按照一定顺序区执行的,但是如何在一个进程中让一些事情同时发生呢?子进程存在缺陷 --引进多线程 --线程:有时又称轻量级进程,程序执行 ...

  6. jz_2440_电阻屏触摸驱动

    驱动中: 入口函数init内: /* 1. 分配一个input_dev结构体 */ s3c_ts_dev = input_allocate_device(); /*------------------ ...

  7. 20145234黄斐《Java程序设计》第一周学习总结

    教材学习内容总结 1.java是SUN公司推出的面相网络的编程语言,当时取名Oak,后改名Java. 2.Java三大平台分别为:Java SE.Java EE与Java ME. 3.Java不仅是程 ...

  8. 北京Uber优步司机奖励政策(1月12日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  9. [数据结构]_[C/C++]_[链表的最佳创建方式]

    场景 1.链表在C/C++里使用非常频繁, 因为它非常使用, 可作为天然的可变数组. push到末尾时对前面的链表项不影响. 反观C数组和std::vector, 一个是静态大小, 一个是增加多了会对 ...

  10. spring源码-BeanFactoryPostProcessor-3.2

    一.BeanFactoryPostProcessor这个是spring容器的拓展之一,其目的是在容器初始化完成之前,通过beanFactory对上下文进行进行操作. 二.常用场景,需要对beanDef ...