Linq有很多值得学习的地方,这里我们主要介绍Linq lambda表达式,包括介绍Expression tree等方面。
Linq lambda表达式
了解过C# 3.0的新特性的话应该知道,在C# 3.0中新引入了一个语法结构,称为lambda expression(Linq lambda表达式/匿名函数)。对此尚不了解的也可以到MSDN上看看,Linq lambda表达式。Linq lambda表达式既可以赋值给一个委托(delegate)类型,例如Action、Func等系列的内建委托类型;也可以赋值给Expression<TDelegate>类型,例如以下Linq lambda表达式:
- x => -x
当它被直接赋值给Func<int, int>类型的变量时,C#编译器会将它的内容编译为一个静态方法,并创建一个对应类型的引用赋值给变量。
- static class Program {
- static void Main( string[ ] args ) {
- Func<int, int> negateFunc = x => -x;
- }
- }
C#编译器会编译为类似下面的代码:
- internal static class Program
- [CompilerGenerated]
- private static int <Main>b__0( int x ) {
- return -x;
- }
- private static void Main( string[ ] args ) {
- Func<int, int> negateFunc = new Func<int, int>( <Main>b__0 );
- }
- }
(实际上还涉及到缓存那个委托,这里省略掉了。另外,之所以会编译为一个静态方法是因为这个Linq lambda表达式没有使用任何“自由变量”,也就是既不是参数或局部变量也不是类的成员的变量。在现有的C#编译器实现中,如果一个匿名函数使用了“this”,那么对应生成的方法会是成员方法;如果使用了其它自由变量的话则会生成一个私有内部类来存放匿名函数所使用到的自由变量,并在这个内部类里生成匿名函数对应的方法。这里作为例子选择了最简单的情况来介绍。)
如此将一个Linq lambda表达式编译为一个实际的函数后,其中的MSIL字节码可以为CLR所理解并执行。这样就足够实现in-memory query了,例如LINQ-to-Objects、LINQ-to-DataSet等。但其它平台无法理解MSIL,要对函数进行分析然后执行就会十分困难。例如说,如果想让一个Linq lambda表达式在SQL Server上执行,该如何让SQL Server也理解它呢?
Expression tree与Linq lambda表达式
MSIL之所以不便于分析是因为它将原本是树状结构的程序代码转换为了线性结构,损失了一些信息,主要是损失了程序代码的“结构性”,更接近于底层而降低了抽象程度。
我们知道,程序源代码对应着具体语法树(concrete syntax tree),每个叶节点对应着代码里的一个词素,其上则是各种语法结构,如表达式、语句、声明、定义等。抽象语法树(abstract syntax tree,AST)则在具体语法树的基础上将一些诸如关键字、括号等冗余信息去掉,让树更加整洁,便于分析而不损失任何有用的信息。
【编辑推荐】