前言

打算整理c# 代码简化史系列,所以相关的整理一下,简单的引出一下概念。

什么是表达式树呢?

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

这个是什么意思呢?用结构表示代码? 用静态的表示动态的,一般来说是某种约定。

比如计算机中的强弱电路,可能这样不好理解。举一个盒子的例子:

假设我要计算加法,那么如果表示加法呢?我用一个盒型结构,把第一个数放在第一个位置,把第二个数放在第二个位置,然后第三个位置我传入方法,表示第一个和第二个会执行第三个位置的方法,在这里呢,还是结构,因为并没有去运行,只是说组合了这样一种结构。

现在呢,假设按照某种约定组合成一种结构,那么这种就称为表达式,就是用来表示某种情况的嘛。然后呢,现在这种表达式是树,那么就叫表达式树了。

这里介绍一下表达式,来增强一波:

然后再来透析一波:

正文

用一个例子来表示正则表达吧,例子是官网的,但是官网解释的比较含糊,所以再来解释一波吧。

官网用的一个例子是:Where(company => (company.ToLower() == "coho winery" || company.Length > 16))

那么来看一下吧:

string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
"Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
"Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
"Blue Yonder Airlines", "Trey Research", "The Phone Company",
"Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" }; // The IQueryable data to query.
IQueryable<String> queryableData = companies.AsQueryable<string>();

有一个数组,然后转换成IQueryable 格式,这么做的目的其实就是因为queryable 实现了一些expression的属性。

好吧,暂时就不解释这几个参数的作用,后面看下去自然就明白了。

接着放代码:

ParameterExpression pe = Expression.Parameter(typeof(string), "company");

这个意思就是说创建了一个属性是company的变量,相当于我们以前的xy,名字随便取。

Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
Expression right = Expression.Constant("coho winery");
Expression e1 = Expression.Equal(left, right);

因为其实树结构,那么这里的参数left就是左子树,right 就是右子树。

left 呢,这个call 就是说pe(也就是company变量)将会执行一个方法,ToLower,对应的就是company.ToLower()。

然后右边就是一个固定的参数coho winery,现在的表达式就是company.ToLower()=='coho winery',返回的是一个bool类型。

left = Expression.Property(pe, typeof(string).GetProperty("Length"));
right = Expression.Constant(16, typeof(int));
Expression e2 = Expression.GreaterThan(left, right);

接下来就是就是获取compay的属性Length,然后和int 类型相比,就是conpany.length>16

Expression predicateBody = Expression.OrElse(e1, e2);

那么就是e1和e2相连,中间用的是or,company.ToLower() == "coho winery" || company.Length > 16 好的现在表达式完了,那么如何和数据联系在一起呢?

// Create an expression tree that represents the expression
// 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))'
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { queryableData.ElementType },
queryableData.Expression,
Expression.Lambda<Func<string, bool>>(predicateBody, new ParameterExpression[] { pe }));
// ***** End Where ***** // ***** OrderBy(company => company) *****
// Create an expression tree that represents the expression
// 'whereCallExpression.OrderBy(company => company)'
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { queryableData.ElementType, queryableData.ElementType },
whereCallExpression,
Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
// ***** End OrderBy ***** // Create an executable query from the expression tree.
IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);

whereCallExpression 和 orderByCallExpression 表示要执行的操作,用whereCallExpression 举例目标类型是Queryable,调用where,然后表达式是predicateBody,参数是pe。

orderByCallExpression 类推。

最后一步就是传递表达:IQueryable results = queryableData.Provider.CreateQuery(orderByCallExpression);

里面的实现是非常复杂的,我自己也没有去看,因为觉得没有必要,这种东西就是一个工具,谁要是这样写,那可正是思维逻辑不是一般的强,一般来说和汇编差不多。

需要明白的就是它不会立即去执行,而是就是一个表达式和其紧密连接。有兴趣可以去了解iqueryable的实现,复杂的一批。

那么不管其多么复杂,就是本质上就是制定一套规则,我们按照它这个规则然后给我们填充,那么就会对应相应的结果给我们,我们可能设计不出这么好的表达式,但是有时候我们也会去制作相应的规则,比如说某种格式等,但你不要去想想它的代码多优雅,因为其稳定性很高,不需要追求优雅。

那么我们为了延后实现,我们就要去这样做吗?如果这样做的话,我想很多人会设计出另外一套,没有这么繁琐,可能就是几个参数,然后一个委托组合成一颗树,虽然很大的局限性,但是写这样的代码真的痛苦。

这个时候人们就想有没有什么能中间转换一下的呢?比如说我写一串字符,然后我就自动按照某种规则去解析不就可以了,但是这种有一个很不好的地方在于,字符串是弱类型调试相当麻烦,这时候就瞄准好了lambda了。

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

C# 编译器只能从表达式 Lambda(或单行 Lambda)生成表达式树。 它无法解析语句 lambda (或多行 lambda)。

举个例子:

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

就可以使用lambda表达式进行一个expression的转换。

从Expression到Expression 之间呢,还有一层,他们的继承关系是

 Object
Expression
LambdaExpression
Expression<TDelegate>

很多时候lambda 表达式转换的表达式就可以为我们解决大部分问题,但是不要觉得这是Expression的全部,因为转换的只有一行,然后expression还有很多是无法用lambda来表示的。

未完待续。

c# 表达式树(一)的更多相关文章

  1. 再讲IQueryable<T>,揭开表达式树的神秘面纱

    接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个风生水起,感觉不整个自己的orm都不好意思继续混博客园了(开个玩笑).那么 ...

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

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

  3. 轻量级表达式树解析框架Faller

    有话说 之前我写了3篇关于表达式树解析的文章 干货!表达式树解析"框架"(1) 干货!表达式树解析"框架"(2) 干货!表达式树解析"框架" ...

  4. 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树

    这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托,表达式树这些应用.今天我尝试用简单的方法叙述一下,让大家在五 ...

  5. LinqToDB 源码分析——处理表达式树

    处理表达式树可以说是所有要实现Linq To SQL的重点,同时他也是难点.笔者看完作者在LinqToDB框架里面对于这一部分的设计之后,心里有一点不知所然.由于很多代码没有文字注解.所以笔者只能接合 ...

  6. LinqToDB 源码分析——生成表达式树

    当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开I ...

  7. 干货!表达式树解析"框架"(1)

    最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 关于我和表达式树 其实我也没有深入了解表达式树一些内在实现的原理 ...

  8. 干货!表达式树解析"框架"(2)

    最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 为了过个好年,我还是赶快把这篇完成了吧 声明 本文内容需要有一定 ...

  9. 干货!表达式树解析"框架"(3)

    最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 这应该是年前最后一篇了,接下来的时间就要陪陪老婆孩子了 关于表达 ...

  10. Lind.DDD.ExpressionExtensions动态构建表达式树,实现对数据集的权限控制

    回到目录 Lind.DDD框架里提出了对数据集的控制,某些权限的用户为某些表添加某些数据集的权限,具体实现是在一张表中存储用户ID,表名,检索字段,检索值和检索操作符,然后用户登陆后,通过自己权限来构 ...

随机推荐

  1. C语言普通写法实现:针对多次同步失败的节能处理机制

    程序不美, 不来一一整理了. 以后有时间可以把这个功能封装为一个类的对象来操作.即使不封装为类,至少也该封装为一个独立的函数吧... 关键代码摘要如下: 无线同步信号发射端,每分钟发一次,每次发射的时 ...

  2. C 清空输入缓冲区,以及fflush(stdin)的使用误区和解决方法

    转载:https://blog.csdn.net/Veniversum/article/details/62048870 对C 语言初学者来说,fflush(stdin)函数被解释为会清空输入缓冲区的 ...

  3. 下载 Oracle Database XE 11gR2

    操作系统:Windows 10 x64 第一节:下载 Oracle Database XE 11gR2 第二节:安装.验证安装 Oracle Database XE 11gR2 第三节:Oracle ...

  4. 【题解】 [GZOI2017]小z玩游戏

    题目戳我 \(\text{Solution:}\) 考虑建图.操作可以看作对\(1\)进行的操作,于是有以下运行过程: \(1\to w[i]\to e[i]\to...\) 考虑倍数,一个数可以走到 ...

  5. 8.Android-简单的登录案例编写

    本章来学习登录案例,由于还未学习自定义控件外观,所以ui界面先用最简单的,并保存登录账号密码到data/data/包名/files下 1.学习之前需要掌握的Context类(通过Context来往AP ...

  6. DM9000网卡驱动分析(转)

    s3c6410自带的DM9000网卡驱动也是基于platform设备模型. 其定义的设备资源在arch/arm/mach-s3c64xx/mach-smdk6410中.有网卡的resource res ...

  7. 2016年 实验二、C2C模拟实验

    实验二.C2C模拟实验 [实验目的] 掌握网上购物的基本流程和C2C平台的运营 [实验条件] ⑴.个人计算机一台 ⑵.计算机通过局域网形式接入互联网. (3).奥派电子商务应用软件 [知识准备] 本实 ...

  8. 多测师讲解自动化测试 _RF封装_(三层模式)高级讲师肖sir

    rf自动化:分层作用: 1.项目----有重复的流程(借款-登录,出借-登录) 2.借款--登录(8个流程)机器人:案例层(用例)写在机器人中,1个机器人=1条用例 分三个层次: 1.案例层(存放用例 ...

  9. keccak和sha3的区别

    keccak应用 在以太坊中,用keccak哈希算法来计算公钥的256位哈希,再截取这256位哈希的后160位哈希作为地址值. keccak和sha3的区别 sha3由keccak标准化而来,在很多场 ...

  10. pytest文档40-pytest.ini配置用例查找规则(面试题)

    前言 面试题:pytest如何执行不是test开头的用例?如执行 xxx_*.py这种文件的用例. pytest.ini 配置文件可以修改用例的匹配规则. pytest命令行参数 cmd打开输入pyt ...