C# 表达式树讲解(一)
一、前言
一直想写一篇Dpper的定制化扩展的文章,但是里面会设计到对Lambda表达式的解析,而解析Lambda表达式,就必须要知道表达式树的相关知识点。我希望能通过对各个模块的知识点或者运用能够多一点的讲解,能够帮助到园友了解得更多。虽然讲解得不全面,如果能成为打开这块的一把钥匙,也是蜗牛比较欣慰的。
表达式系列目录
C# 表达式树讲解(一)
二、表达树理解
表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,它将我们原来可以直接由代码编写的逻辑以表达式的方式存储在树状的结构里,从而可以在运行时去解析这个树,然后执行,实现动态的编辑和执行代码。在.Net 里面的Linq to SQL就是对表达式树的解析。
这里先讲解下表达式和表达式树,表达式相信大家都知道,比如x+5或者5,都可以算是表达式,而表达式树里面的树指的二叉树,也就是表达式的集合,C#中的Expression类就是表达式类。对于一棵表达式树,其叶子节点都是参数或者常数,非叶子节点都是运算符或者控制符。
2.1、表达式的创建
Lambda表达式方法:
Expression<Func<int, int,bool>> fun = (x, y) => x < y
这种方法创建出的表达式根节点类型为ExpressionType.Lambda,Type类型为返回值类型typeof(bool)
组装法(通过 API 创建表达式树):
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 });
我们先创建了两个参数表达式num和5,然后用LessThan组装在一起,最终的表达式为“num<5”,expr的节点类型为LessThan,Type类型为typeof(bool)
我们先看看表达式树里面的构造
首先Expression<TDelegate>的功能是将强类型Lambda表达式表示为表达式树形式的数据结构,他的父类是LambdaExpression,比较他们代码可知,Lambda表达式的主体,名称和参数全部保存在LambdaExpression里面。
Expression<TDelegate>与LambdaExpression代码截图:


LambdaExpression里面的Body就是我们的表达式。
C#表达式给我们提供了丰富的表达式类,进入到LambdaExpression类里面

方法返回类型以“Expression”结尾的,基本上都是一个表达式类。
每个表达式代表的定义和创建方法,可以参照微软官方文档https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.binaryexpression?view=netframework-4.8
下面是平常使用最多的表达式
ConstantExpression:常量表达式
ParameterExpression:参数表达式
UnaryExpression:一元运算符表达式
BinaryExpression:二元运算符表达式
TypeBinaryExpression:is运算符表达式
ConditionalExpression:条件表达式
MemberExpression:访问字段或属性表达式
MethodCallExpression:调用成员函数表达式
Expression<TDelegate>:委托表达式
2.2、表达式的解析
表达式树解析
通过LambdaExpression类我们可以知道,表达式树包含:参数[Parameters],表达式树类型[NodeType],表达式[Body],返回类型[ReturnType],Lambda表达式的委托[Compile]以及Lambda表达式名称[name],如图所示:

表达式解析:
所有的表达式都包含:左节点【Left】,右节点【Right】,类型【NodeType】,不同的表达式还会有其他属性,这里的左右节点依旧是表达式。
下图是BinaryExpression表达式截图

表达式树和表达式里面的类型NodeType是一个枚举,一共有85个类型,有兴趣的朋友可以去了解下。
常用的类型如下:
ExpressionType.And:C#中类似于&
ExpressionType.AndAlso:C#中类似于&&
ExpressionType.Or:C#中类似于|
ExpressionType.OrElse:C#中类似于||
ExpressionType.Equal:C#中类似于==
ExpressionType.NotEqual:C#中类似于!=
ExpressionType.GreaterThan:C#中类似于>
ExpressionType.GreaterThanOrEqual:C#中类似于>=
ExpressionType.LessThan:C#中类似于<
ExpressionType.LessThanOrEqual:C#中类似于<=
ExpressionType.Add:C#中类似于+
ExpressionType.AddChecked:C#中类似于+
ExpressionType.Subtract:C#中类似于-
ExpressionType.SubtractChecked:C#中类似于-
ExpressionType.Divide:C#中类似于/
ExpressionType.Multiply:C#中类似于*
ExpressionType.MultiplyChecked:C#中类似于*
2.3、编译表达式树
在表达式创建那,我们组合创建了一个Lambda表达式,那么应该怎么使用它呢?在“表达式的解析”里面,LambdaExpression类和Expression<TDelegate>类都有一个Compile的方法,学名是Lambda表达式的委托,其实就是Lambda表达式编译函数的委托,所以我们只需要调用他,得到的结果就是一个函数方法。
代码修改如下:
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 }); Console.WriteLine($"Lambda的内容:{lambda1.ToString()}"); //表达式的编译
var func = lambda1.Compile();
Console.WriteLine($"Lambda的运行结果:{func(6)}");
运行结果

三、总结
这里我们对表达式做了基本的讲解,相信大家对Lambda表达式有了初步的了解,下面我们将继续讲解对一个表达式树的遍历。
C# 表达式树讲解(一)的更多相关文章
- C# 表达式树分页扩展(三)
一.前言 前面我们知道了表达树的基本知识,也明白了怎么遍历和修改一个表达式,这里我们就一个实际的场景来进行功能开发. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) C# 表达式树 ...
- C# 表达式树遍历(二)
一.前言 上一篇我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) ...
- C# 表达式树Lambda扩展(四)
一.前言 本来计算这篇文章在后面需要运用的时候写的,但是既然写到表达式的扩展呢,就一起写完吧. 看到这个标题就有一种疑问,Lambda表达式本来就是表达式树,还需要怎么扩展?那就看看下面的内容,你就知 ...
- Linq系列(7)——表达式树之ExpressionVisitor
大家好,由于今天项目升级,大家都在获最新代码,所以我又有时间在这里写点东西,跟大家分享. 在上一篇的文章中我介绍了一个dll,使大家在debug的时候可以可视化的看到ExpressionTree的Bo ...
- C# 委托、事件、表达式树理解
1.什么是委托? 委托是一种动态调用方法的类型,属于引用型. 委托是对方法的抽象和封装.委托对象实质上代表了方法的引用(即内存地址) 所有的异步都是委托 委托就是函数当入参 委托被各种语法糖遮 ...
- 再讲IQueryable<T>,揭开表达式树的神秘面纱
接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个风生水起,感觉不整个自己的orm都不好意思继续混博客园了(开个玩笑).那么 ...
- [C#] C# 知识回顾 - 表达式树 Expression Trees
C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...
- 轻量级表达式树解析框架Faller
有话说 之前我写了3篇关于表达式树解析的文章 干货!表达式树解析"框架"(1) 干货!表达式树解析"框架"(2) 干货!表达式树解析"框架" ...
- 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树
这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托,表达式树这些应用.今天我尝试用简单的方法叙述一下,让大家在五 ...
随机推荐
- 100天搞定机器学习|day43 几张GIF理解K-均值聚类原理
前文推荐 如何正确使用「K均值聚类」? KMeans算法是典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大.该算法认为簇是由距离靠近的对象组成的,因此把 ...
- 导入spark2.3.3源码至intellij idea
检查环境配置 maven环境 2.检查scala插件 没有的话可以到https://plugins.jetbrains.com/plugin/1347-scala/versions 下载与idea对应 ...
- Linux权限管理(7)
权限的基本介绍: rwx权限详解: rwx作用到文件: [r]:代表可读,可以读取.查看 [w]:代表可写,可以修改,但不代表可以删除该文件,删除一个文件的前提条件是对该文件所在的目录有写权限才能删除 ...
- python所有的内置异常类型汇总
内置异常基类 在 Python 中,所有异常必须为一个派生自 BaseException 的类的实例. 通过子类化创建的两个不相关异常类永远是不等效的,既使它们具有相同的名称. 下列异常主要被用作其他 ...
- maven项目编译通过,测试用例卡住,断点也用不了
maven项目编译通过,测试用例卡住,断点也用不了.如下图 maven的tomcat插件可以运行没报错,但是网页访问一直转圈 原因: 最后发现是typeAliasesPackage这里设置了别名,所以 ...
- HBase 系列(十)—— HBase 的 SQL 中间层 Phoenix
一.Phoenix简介 Phoenix 是 HBase 的开源 SQL 中间层,它允许你使用标准 JDBC 的方式来操作 HBase 上的数据.在 Phoenix 之前,如果你要访问 HBase,只能 ...
- Keras实例教程(3)
https://blog.csdn.net/baimafujinji/article/details/80705578
- [C#] 改进SqliteHelper, 减少拼接SQL语句
说明:开始几个是基本的方法,后面稍微封装了下 public class SqliteHelper { //连接字符串 private static readonly string str = Conf ...
- Socket I/O模型之select模型
socket网络编程中有多种常见的I/O模型: 1.blocking阻塞 2.nonblocking非阻塞 3.I/O multiplexing复用 4.signal driven 5.asynchr ...
- .NET CORE下最快比较两个文件内容是否相同的方法 - 续
.NET CORE下最快比较两个文件内容是否相同的方法 - 续 在上一篇博文中, 我使用了几种方法试图找到哪个是.NET CORE下最快比较两个文件的方法.文章发布后,引起了很多博友的讨论, 在此我对 ...