在很早之前就据说过表达式树了,但并无去了解它。虽然本身用过linq to sql和linq to entity,但也就用着就用着,并无去深究c#代码怎么会生成sql代码而不是IL。废话很少说了,开写吧!sql
.net里表达式树核心概念就是:将代码做为数据。它将一些代码表示为一个对象树,树中的每一个节点自己都是一个表达式,不一样的表达式类型表明能在代码中执行不一样操做:二元操做,一元操做,方法调用等等。express
System.Linq.Expressions命名空间包含了表明表达式的各个类。全部的表达式类都从Expression类派生,Expression是个抽象类,主要包含的是一些静态的方法,这些方法用于生成其余表达式类的实例。Expression类还包含了两个重要属性:c#
1.Type属性:表明了表达式求值结果的类型。好比,一个表达式是要获取一个字符串的Length属性,那么该表达式的Type属性应为int类型spa
2.NodeType属性:表明了表达式的种类。这个种类表示成ExpressionType枚举的一个成员:LessThan,Invoke,Multiply,MemberAccess等等(有80几种,汗!)。.net
一个表达式树的简单例子 code
static void Main(string[] args) { Expression firstArg = Expression.Constant(2); Expression secondArg = Expression.Constant(4); Expression add = Expression.Add(firstArg, secondArg); Console.WriteLine(add); }
输出结果:对象
上面的代码将会生成以下图的表达式树:blog
值得注意的是,表达式中“叶”表达式在代码中是最早建立的:表达式是自下而上构建的。这是由“表达式不易变”这一事实实现的。ip
将表达式树编译成委托 字符串
LambdaExpression是从Expression派生的类型。泛型类Expression<TDelegate>是从LambdaExpression派生的,其中泛型参数TDelegate必须是委托类型。
LambdaExpression有个Compile方法能建立恰当类型的一个委托。而Expression<TDelegate>的Compile方法返回TDelegate类型的委托。来看看下面的例子:
static void Main(string[] args) { Expression firstArg = Expression.Constant(2); Expression secondArg = Expression.Constant(4); Expression add = Expression.Add(firstArg, secondArg); Expression<Func<int>> func = Expression.Lambda<Func<int>>(add); Func<int> compiled = func.Compile(); Console.WriteLine(compiled()); }
咱们经过Expression.Lambda<TDelegate>(Expression expression)方法来建立Expression<TDelegate>类型对象,再调用其Compile方法获取表达式树编译出的委托实例。
将C# Lambda表达式转换成表达式树
咱们知道Lambda表达式能显示或隐式地转换成恰当的委托实例。可是,编译器也能很轻松的将Lambda表达式构建为一个表达式树:
//将Lambda表达式转换成表达式树 Expression<Func<int>> return5 = () => 5;
可是,并非全部的Lambda表达式都能转换成表达式树,有一些限制:不能将带有一个语句块的Lambda转换成一个表达式树-----只有对单个表达式进行求值得Lambda才能够。表达式中不能包含赋值操做,由于表达式树中表示不了这种操做。还有其余一些较少见的限制,总而言之,若是存在转换问题,你会在编译时发现。
位于Linq核心的表达式树
表达式树能够说是Linq的核心之一,为何是Linq的核心之一呢?由于表达式树使得c#再也不是仅仅能编译成IL,咱们能够经过c#生成一个表达式树,将结果做为一个中间格式,在将其转换成目标平台上的本机语言。好比SQL。咱们经常使用的Linq to sql就是这样生成SQL的。
下图展现了Linq to Objects和Linq to SQL的不一样路径:
资料参考于《深刻理解c#》第二版