【C#复习总结】细说表达式树
1 前言
系列5:细说表达式树
系列6:细说事件
涛声依旧,再续前言,接着用大佬的文章作为开头。
表达式树其实与委托已经没什么关系了,非要扯上关系,那就这么说吧,表达式树是存放委托的容器。如果非要说的更专业一些,表达式树是存取Lambda表达式的一种数据结构。要用Lambda表达式的时候,直接从表达式中获取出来,Compile()就可以直接用了。如下代码:
using System;
using System.Linq.Expressions; namespace Expressions
{
class Program
{ static void Main(string[] args)
{
Expression<Func<int, int, int>> exp = (x, y) => x + y;
Func<int, int, int> fun = exp.Compile();
int result = fun(, ); Console.WriteLine("{0}",result);
Console.ReadKey();
}
}
}
2 什么是表达式树
.NET 3.5中新增的表达式树(Expression Tree)特性,第一次在.NET平台中引入了“逻辑即数据”的概念。也就是说,我们可以在代码里使用高级语言的形式编写一段逻辑,但是这段逻辑最终会被保存为数据。正因为如此,我们可以使用各种不同的方法对它进行处理。例如,您可以将其转化为一个SQL查询,或者外部服务调用等等,这便是LINQ to Everything在技术实现上的重要基石之一。
表达式树也称表达式目录树,将代码以一种抽象的方式表示成一个对象树,树中每个节点本身都是一个表达式。表达式树不是可执行代码,它是一种数据结构。
3 表达式树的使用
3.1 构建表达式树
System.Linq.Expressions命名空间中包含了代表表达式的各个类,所有类都从Expression派生,我们可以通过这些类中的静态方法来创建表达式类的实例。Expression类包括两个重要属性:
- Type属性代表求值表达式的.NET类型,可以把它视为一个返回类型
- NodeType属性返回所代表的表达式的类型
下面看一个构建表达式树的简单例子:
Expression numA = Expression.Constant();
Console.WriteLine("NodeType: {0}, Type: {1}", numA.NodeType, numA.Type);
Expression numB = Expression.Constant();
Console.WriteLine("NodeType: {0}, Type: {1}", numB.NodeType, numB.Type); BinaryExpression add = Expression.Add(numA, numB);
Console.WriteLine("NodeType: {0}, Type: {1}", add.NodeType, add.Type); Console.WriteLine(add);
Console.Read();
通过例子可以看到,我们构建了一个(6+3)的表达式树,并且查看了各个节点的Type和NodeType属性。
Expression有很多派生类,有很多节点类型。例如,BinaryExpression就代表了具有两个操作树的任意操作。这正是NodeType属性重要的地方,它能区分由相同的类表示的不同种类的表达式。其他的节点类型就不介绍了,有兴趣可以参考MSDN。
对于上面的例子,可以用下图描述生成的表达式树,值得注意的是,”叶子”表达式在代码中是最先创建的,,表达式是自下而上构建的。表达式是不易变的,所有可以缓存和重用表达式。
3.2 将表达式编译成委托
LambdaExpression是从Expression派生的类型之一。泛型类型Expression<TDelegate>又是从LambdaExpress派生的。

Expression和Expression<TDelegate>的区别在于,泛型类以静态类型的方式标志了它是什么种类的表达式,也就是说,它确定了返回类型和参数。例如上面的加法例子,返回值是一个int类型,没有参数,所以我们可以使用签名Func<int>与之匹配,所以可以用Expression<Func<int>>以静态类型的方式来表示该表达式。
这样做的目的在于,LambdaExpression有一个Compile方法,该方法能创建一个恰当类型的委托。 Expression<TDelegate>也有一个同名方法,该方法可以返回TDelegate类型的委托。获得了委托之后,我们就可以使 用普通委托实例调用的方式来执行这个表达式。
接着上面加法的例子,我们把上面的加法表达式树转换成委托,然后执行委托:
Func<int> addDelegate = Expression.Lambda<Func<int>>(add).Compile();
Console.WriteLine(addDelegate());
从这个例子中我们看到怎么构建一个表达式树,然后把这个对象树编译成真正的代码。在.NET 3.5中的表达式树只能是单一的表达式,不能表示完整的类、方法。这在.NET 4.0中得到了一定的改进,表达式树可以支持动态类型,我们可以创建块,为表达式赋值等等。
3.3 将Lambda表达式转换为表达式树
Lambda表达式不仅可以创建委托实例,C# 3.0对于将Lambda表达式转换成表达式树提供了内建的支持。我们可以通过编译器把Lambda表达式转换成一个表达式树,并创建一个Expression<TDelegate>的一个实例。
下面的例子中我们将一个Lambda表达式转换成一个表达式树,并通过代码查看表达式树的各个部分:
static void Main(string[] args)
{
//将Lambda表达式转换为类型Expression<T>的表达式树
//expression不是可执行代码
Expression<Func<int, int, int>> expression = (a, b) => a + b; Console.WriteLine(expression);
//获取Lambda表达式的主体
BinaryExpression body = (BinaryExpression)expression.Body;
Console.WriteLine(expression.Body);
//获取Lambda表达式的参数
Console.WriteLine(" param1: {0}, param2: {1}", expression.Parameters[], expression.Parameters[]);
ParameterExpression left = (ParameterExpression)body.Left;
ParameterExpression right = (ParameterExpression)body.Right;
Console.WriteLine(" left body of expression: {0}{4} NodeType: {1}{4} right body of expression: {2}{4} Type: {3}{4}", left.Name, body.NodeType, right.Name, body.Type, Environment.NewLine); //将表达式树转换成委托并执行
Func<int, int, int> addDelegate = expression.Compile();
Console.WriteLine(addDelegate(, ));
Console.Read();
}
4 总结
前面看到,通过Expression的派生类中的各种节点类型,我们可以构建表达式树;然后可以把表达式树转换成相应的委托类型实例,最后执行委托实例的代码。但是,我们不会绕这么大的弯子来执行委托实例的代码。
表达式树主要在LINQ to SQL中使用,我们需要将LINQ to SQL查询表达式(返回IQueryable类型)转换成表达式树。之所以需要转换是因为LINQ to SQL查询表达式不是在C#代码中执行的,LINQ to SQL查询表达式被转换成SQL,通过网络发送,最后在数据库服务器上执行。
参考文献:http://blog.jobbole.com/84588/
推荐阅读:腾飞老大的 由浅入深表达式树
【C#复习总结】细说表达式树的更多相关文章
- asp.net core 排序过滤分页组件:sieve(2)表达式树的复习
在Sieve组件中使用了很多关于表达式树的知识,但在我们日常的工作中写表达式树的机会是非常少的,至少在我的编程生涯中没怎么写过表达式树(可能也就是3,4次).所以,为了能够看懂Sieve里面的源代码, ...
- C#复习笔记(4)--C#3:革新写代码的方式(Lambda表达式和表达式树)
Lambda表达式和表达式树 先放一张委托转换的进化图 看一看到lambda简化了委托的使用. lambda可以隐式的转换成委托或者表达式树.转换成委托的话如下面的代码: Func<string ...
- 表达式树练习实践:C#值类型、引用类型、泛型、集合、调用函数
目录 表达式树练习实践:C#值类型.引用类型.泛型.集合.调用函数 一,定义变量 二,访问变量/类型的属性字段和方法 1. 访问属性 2. 调用函数 三,实例化引用类型 四,实例化泛型类型于调用 五, ...
- Asp.net Core C#进行筛选、过滤、使用PredicateBuilder进行动态拼接lamdba表达式树并用作条件精准查询,模糊查询
在asp.net core.asp.net 中做where条件过滤筛选的时候写的长而繁琐不利于维护,用PredicateBuilder进行筛选.过滤.LInq配合Ef.core进行动态拼接lamdba ...
- 再讲IQueryable<T>,揭开表达式树的神秘面纱
接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个风生水起,感觉不整个自己的orm都不好意思继续混博客园了(开个玩笑).那么 ...
- [C#] C# 知识回顾 - 表达式树 Expression Trees
C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...
- 轻量级表达式树解析框架Faller
有话说 之前我写了3篇关于表达式树解析的文章 干货!表达式树解析"框架"(1) 干货!表达式树解析"框架"(2) 干货!表达式树解析"框架" ...
- 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树
这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托,表达式树这些应用.今天我尝试用简单的方法叙述一下,让大家在五 ...
- LinqToDB 源码分析——处理表达式树
处理表达式树可以说是所有要实现Linq To SQL的重点,同时他也是难点.笔者看完作者在LinqToDB框架里面对于这一部分的设计之后,心里有一点不知所然.由于很多代码没有文字注解.所以笔者只能接合 ...
随机推荐
- 用户不在 sudoers 文件中,此事将被报告
在使用Linux系统过程中,通常情况下,我们都会使用普通用户进行日常操作,而root用户只有在权限分配及系统设置时才会使用,而root用户的密码也不可能公开.普通用户执行到系统程序时,需要临时提升权限 ...
- (三)版本控制管理器之CVS(下)
在上一篇文章<(二)版本控制管理器之CVS(上)>中,我为大家介绍了什么是CVS.CVS的特点.CVS的安装.CVSNT服务器的配置.TortoiseCVS客户端的配置等,本篇文章继续为大 ...
- ORA-12514, TNS:listener does not currently know of service requested in connect descriptor案例2
今天使用SQL Developer连接一台测试服务器数据库(ORACLE 11g)时,遇到了"ORA-12514, TNS:listener does not currently know ...
- Win7下安装OpenSSL出现的问题
1. cl.exe 运行出现错误,提示“丢失mspdb100.dll”等字样,需要将 C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE ...
- Ubuntu下启动 Redis时, 提示 "Can't open the log file: Permission denied failed"
问题来源:在删除var目录下的log文件时,将redis文件夹删除了.然后在重启时:/etc/init.d/redis-server start,提示: Starting redis-server: ...
- 我的游戏学习日志3——三国志GBA
我的游戏学习日志3——三国志GBA 三国志GBA由日本光荣公司1991~1995所推出<三国志>系列游戏,该作是光荣在GBA上推出的<三国志>系列作品的第一款.本游戏登场武将总 ...
- Java中使用elasticsearch搜索引擎实现简单查询、修改等操作-已在项目中实际应用
以下的操作环境为:jdk:1.8:elasticsearch:5.2.0 maven架包下载坐标为: <dependency> <groupId>org.elasticsear ...
- Ubuntu 普通用户无法启动Google chrome
删除 /home/你的用户名/.config/google-chrome文件,再打开就好了 root@ecos:cd /home/ecos/.config root@ecos:~/.config# r ...
- margin-left:-20px
对于负的margin值,设置其作用时要和position:absolute;一起使用
- PostgreSQL条件表达式 case when then end
例: SELECT CASE WHEN (store_size <= (100)::NUMERIC) THEN '小店'::TEXT WHEN (store_size >= (200):: ...