表达式树
以前没听过表达式树,只听过表达式,而且听过Lambda
表达式,首先介绍一下.NET
里表达式树的核心概念:讲代码作为数据,他将一些代码表示为一个对象树,树中的每个节点本身都是一个表达式,不同的表达式类型代表能在代码中执行不同操作:二元操作,一元操作,方法调用等等.
System.Linq.Expression
命名空间包含了代表表达式的各个类.所有的表达式类都从Expression
类派生,Expresssion
是个抽象类,主要包括的是一些静态方法,这些方法用于生成其他表达式类的实例.Expression
类还包含了两个重要属性:
Type
属性:代表了表达式求值结果的类型.比如,一个,一个表达式是要获取一个字符串的Length
属性,那么该表达式的Type
属性应为int
类型.NodeType
属性:代表了表达式的种类.这个种类表示成Expression Type
美剧的一个成员:LessThan,Invoke,Multiply,MemberAccess
(好像有80多种).
案例:
Expression firstArg = Expression.Constant(2);
Expression secondArg = Expression.Constant(4);
Expression add = Expression.Add(firstArg, secondArg);
Console.WriteLine(add);
//输出结果为 : <2 + 4>
Console.ReadKey();
分析:上述表达式会生成如下表达式:
我突然这么一说,你肯定觉得我在忽悠你,我骗人,像我这样的正人君子不会骗你的.表达式中的”叶”表达式在代码中是最先创建的:表达式时自下而上构建的.这是由”表达式不易变”这一事实实现的.
将表达式树编译成委托
LambdaExpression
是从Expression
派生的类型.泛型类Expression<TDelegate>
是从LamdaExpression
派生的,其中反省参数TDelegate
必须是委托类型.
LambdaExpression
有个Comlile
方法能创建恰当类型的一个委托.而Expression<TDelegate>
的Compile
方法返回TDelegate
类型的委托.案例如下:
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);
Console.ReadKey();
分析:我们通过Expression.Lambda<TDelegate>(Expression expression)
方法来创建Expression<TDelegate>
类型对象,再调用其Complie
方法获取表达式树编译出的委托实例.
将C# Lambda表达式转换成表达式树
我们知道Lambda
表达式能显示或隐式的转换成恰当的委托实例.但是,编译器也能轻松的将Lambda
表达式构建成一个表达式树:
将Lambda
表达式转换成表达式树
Expression <Func<int>> return 5=()=>5;
但是,并不是所有的Lambda
表达式都能转换成表达式树,有一些限制:不能将带有一个语句块的Lambda
转换成一个表达式树—-只有对的那个表达式进行求值的Lambda
才可以.表达式中不能包含赋值操作,因为表达式树中表示不了这种操作.还有其他一些较少见的限制,总而言之,如果存在转换问题,会在编译时发现.
位于LINQ核心的表达式树
表达式树可以说是LINQ
的核心之一,为什么是LINQ
的核心之一呢?因为表达式树使得C#
不再是仅仅能编译成IL
,我们可以通过C#
生成一个表达式树,将结果作为一个中间格式,在将其转换成目标平台上的本机语言.比如SQL
.我们常用的LINQ to SQL
就是这样生成的.
下图展示了LINQ to Objects
和LINQ to SQL
的不同路径
表达式树的用途:
通过Expression
的派生类中的各种节点类型,我们就可以构建表达式树;然后可以把表达式树转换成相应的委托类型实例,最后执行委托实例的代码,但是我们不会绕这么大的弯子来执行委托实例的代码.
表达式树主要在LINQ to SQL
中使用,我们需要将LINQ to SQL
查询表达式(返回Queryable
类型)转换成表达式树.之所以需要转换是因为LINQ to SQL
查询表达式不是在C#
代码中执行的,LINQ to SQL
查询表达式被转换成SQL
,通过网络发送,最后在数据库服务器中执行.