代码改变世界

不能不说的C#特性-表达式树

2008-09-18 00:00  横刀天笑  阅读(...)  评论(... 编辑 收藏

 最近发生了很多很多事情,频繁的搬家。工作上的事情也挺多的,所以博客更新的非常缓慢。

 

已经有很多很多人聊过这个话题,今天我在这里重复也不会探讨出什么新东西,只是把自己的理解描述出来,更是为了整个系列文章的完整性。

当你听说Linq给你的承诺时,你怎么想的?Wow,我们可以以统一的方式操作各种各样的数据了。这就是我当时的想法。虽然人们在现实中总是喜欢差异,认为差异才能产生美,如果一切的一切都是一样的,这个世界将无比的单调,可是作为程序员的我们却对标准趋之若鹜,对差异嫉恶如仇。看同桌的你是不是正在为了OracleSql Server两种数据库编写两套数据访问的类?

表达式树概念

Linq的承诺貌似Java那个梦想一样:Write Once,Run AnywhereJava是怎么做到的?Sun等公司为我们在各种平台架构上实现了各自的虚拟机,Java的编译分为两个阶段,第一阶段将Java代码编译为字节码,在这个阶段不管在什么平台上,只要Java源代码一样生成的字节码是一致的,第二个阶段,也就是运行阶段,虚拟机会根据平台的不同生成不同的代码。就是通过将编译器分为前端和后端来实现这个梦想。

实际上LINQ也是这么做的,对各种数据的操作无非“增删查改”,但是具体做的时候关系数据库需要使用SQL来操作,而XML需要XPath来操作。我们如何将“增删查改”的语法做到一致,让我们用起来好像操作的数据只有一种?

答案是使用表达式树

表达式树仅仅是将表达式(这里特指Lambda表达式)用树状的数据结构来表示。相当于Java的字节码,至于如何去解析这个树的结构那就看你自己了,如何去解释这个表达式树。

看下面这个Lambda表达式:

username => username == “yuyi”

我们主要看表达式的主体:username == “yuyi”,如果我们是要对数据库进行操作,这将翻译为字段username中所有值为”yuyi”的行,如果操作的是XML那也许是查询名称为username,值为”yuyi”attribute。表达式树承载的只是这样一个结构:

 

如何解释它这是你的事。

C#Expression<Function<string,bool>> IsTrue = username=>username==”yuyi”,就表示一个表达式树,这个语句在编译后就组成为一个树状的结构。

在编译原理中,我们知道编译器的前两个阶段主要做的是:词法分析、语法分析。在词法分析中编译器会从代码文件中读入一个个的字符,然后识别出其中的关键字、标识符、运算符、常量、字符串等。

比如上面的表达式就会识别出:

Username->标识符

==->运算符

“yuyi”->字符串

这些东西就叫做符号

然后以这些符号作为输入进行语法分析,语法分析会将这些符号组合成一个树,这个树我们称之为抽象语法树(AST),表达式树也是一种简单的AST

(在VS2008带的例子中有个DynamicQuery的例子,这里有一个Dynamic.cs文件,这里就自己做表达式的解析,只不过解析的是用字符串形式的表达式,通过这个代码你可以看看表达式的解析过程,这里有一个ExpressionParser类,它就是负责表达式的词法解析的,该类里有一个Token的机构,这就是上面所说的符号,TokenId表示符号的类型

<"Samples"1033"CSharpSamples"LinqSamples"DynamicQuery>

对于Lambda表达式,有两种编译方式,常规的:

Func<string,bool> IsTrue = username => username == “yuyi”;

这个时候编译器会将其编译为匿名方法,关于匿名方法的相关介绍可以参看我这篇文章

编译成表达式树:

Expression<Func<string,bool>> IsTrue = username => username == “yuyi”;

这么细微的差别,C#这次却不真的编译这条语句,而是将其进行词法、语法分析,得到的是一个数据结构。

我们注意到表达式树还有一个Compile实例方法,它可以将表达式编译成匿名方法,也就是这个数据结构可以向匿名方法转变。

想想Linq to SQL,实际上它不就是C#作为源语言,SQL作为目标语言的一个编译过程么。形成表达式树(抽象语法树)后,就是代码生成了,只不过这个代码生成有的时候是生成代码,比如Linq to SQL,生成的是SQL语句,有的时候是生成的方法调用,比如Linq to XML,生成的是对XML文档的操作。

为什么一样的语句,有的时候是操作内存中的对象集合,有的时候是操作远程数据库?请查看我这篇文章

关于表达式树更多细节内容,你可以查看TerryLee老大的这篇图文并茂的文章