表达式树是不可执行的代码,它只是用于表示一种树状的数据结构,树上的每一个节点都表示为某种表达式类型,大概有25种表达式类型,它们都派生自Expression类。创建表达式树具体有两个优势:
1.对表达式树的代码进行编辑修改,使表达式树中的代码变成动态代码,根据不同的数据库修改树上的代码逻辑从而达到动态切换数据库查询语句的目的,用表达式树可以动态构建针对不同数据库的查询语句。
2.完成类似反射访问未知对象的属性,通过动态构造表达式树,生成委托。
三种方式创建表达式树
Expression(表达式类)
此类可以称为表示表达式的类型,或也称为一棵表达式树。因为当你每创建一个表示表达式的实例时,都可以将该类型实例看成是一棵表达式树。每种表示表达式的类型都有一个具体的类型,如Expression的Variable()方法创建的是ParameterExpression类型的表达式,Expression的Add()方法创建的则是BinaryExpression类型的表达式。无论哪种表示表达式的类型都是从Expression派生。
ParameterExpression variable = Expression.Variable ( typeof ( int ) , "x" );
LambdaExpression(Lambda表达式类)
LambdaExpression是Expression的子类,此类型表示一个Lambda表达式类型。
LambdaExpression lambda = Expression.Lambda ( Expression.Variable ( typeof ( int ) , "x" ) );
Expression<TDelegate>(表达式委托类)
此类从LambdaExpression派生,所以也可以使用此类来创建一个表示Lambda表达式的类型,这种方式只能提供一个Lambda表达式,不能提供Lambda语句。
执行表达式
假设创建了一个表示加法运算的Lambda表达式对象,现在想要执行这个表示Lambda表达式的对象,此时就要用到Expression<TDelegate>类,因为此类从LambdaExpression派生,LambdaExpression提供了一个叫做Compile()的方法可以将表示Lambda表达式的对象(LambdaExpression)解析为一个委托,然后你就可以执行Lambda表达式。而要执行表示表达式的对象,也必然需要将其封装到Lambda表达式中,否则不可能执行。封装任何表示表达式的对象都是通过Expression的Lambda<TDelegate>(Expression expr)方法,该方法是非泛型版本Expression.Lambda ( Expression expr )的重载,返回一个Expression<TDelegate>类型的实例。其中TDelegate是一个委托类型,你可以根据封装的表示lambda表达式的对象的代码逻辑来确定需要为Lambda定义一个什么样的委托,可以使用内置的Fun或Action委托表示该LambdaExpression。比如Expression<Func<TDelegate>> | Expression<Action<TDelegate>> | Expression<Action>。只要记住:要执行一个表示表达式的对象就需要使用Expression的Lambda<TDelegate>(Expression expr)方法对其进行封装,使其处于Lambda方法体中以便可以被执行。
var y = Expression.Parameter ( typeof ( int ) , "y" ); //表示定义参数的Expression表达式
var add = Expression.Add ( x , y ); //表示加法运算的Expression表达式
//将表达式封装到表示Lambda的表达式中,因为add是表示计算x+y的表达式,所以Lambda<TDelagate>中的委托应定义为Lambda<Func<int,int,int>>
var lambdaInfo =Expression.Lambda<Func<int,int,int>> ( add, new [ ] { x, y } );
int r = lambdaInfo.Compile ( ) (1,2); //执行
Console.WriteLine ( r ); // print 3
表达式树
无论使用哪种方式创建表达式对象,编译器都会自动为表达式生成一棵树结构,然后将表达式主体的代码体拆分成单一的表达式并作为主体表达式的子节点。变量、参数、运算符都会被拆分成一个单一的表达式,如果被拆分的表达式含有多个子表达式,则子表达式将作为表达式的子节并以此类推。上面第三个例子中创建了一个表示Lambda的表达式,该表达式接收并返回一个int类型的变量,其树结构如下:
上面例子中创建了一个表达式( x , y ) => x != y && x!=0,代码体是x != y && x!=0,&&是一个表达式,它含有左右两个操作数的子表达式,所以它会被拆分,左边x != y是一个表达式,右边 x!=0也是一个表达式,左右两边都含有子表达式,所以会继续拆分,直到无法拆分为止,结构如下:
Expression的子类
树的每个子节点是一个具体的表达式,它们都有自己的表达式类型,这些类型从Expression派生。
BinaryExpression; //二元运算表达式
ConstantExpression; //常量表达式
ParameterExpression; //变量、变量参数表达式
GotoExpression; //跳转语句表达式,如:return。continue、break
BlockExpression; //块语句表达式
ConditionalExpression; //条件语句表达式
LoopExpression; //循环语句表达式
SwitchExpression; //选择语句表达式
IndexExpression; //访问数组索引表达式
MethodCallExpression; //调用方法表达式
LambdaExpression; //Lambda表达式
TypeBinaryExpression; //类型检查表达式
NewArrayExpression; // 创建数组表达式
DefaultExpression; //默认值表达式
DynamicExpression; //动态类型表达式
TryExpression; //try语句表达式
MemberExpression; //类成员表达式
InvocationExpression; //执行Lambda并传递实参的表达式
NewExpression; //调用无参构造函数表达式
MemberInitExpression; //调用带参构造函数表达式,可初始化成员
ListInitExpression; //集合初始化器表达式
ExpressionType枚举
表示乘法运算和表示加法运算的表达式都是属于二元运算,所以这两种表达式都是BinaryExpression类型,但如果需要确定这两个表达式具体的行为,就需要使用Expression.NodeType属性,此属性是一个枚举数,用以获取表达式属于什么种类(根据其行为判定)。
//加法运算,如 a + b, ,不进行溢出检查,针对数值操作数。
AddAssign
//加法复合赋值运算,如 ( a += b), ,不进行溢出检查,针对数值操作数。
AddAssignChecked
//加法复合赋值运算,如 ( a += b), ,进行溢出检查,针对数值操作数。
AddChecked
//加法运算,如 ( a + b), ,进行溢出检查,针对数值操作数。
And
//按位或逻辑 AND 操作,如 ( a & b) 在 C# 和 (a And b) 在 Visual Basic 中。
AndAlso
//在条件 AND 仅当第一个操作数的计算结果为才计算第二个操作数的操作 true。 它对应于 ( a && b) 在 C# 和 (a AndAlso b) 在 Visual Basic 中。
AndAssign
//按位或逻辑 AND 复合赋值运算,如 ( a &= b) C# 中。
ArrayIndex
//索引操作在一维数组中,如 array [ index ] 在 C# 或 array(index) 在 Visual Basic 中。
ArrayLength
//获取一维数组的长度,如操作 array.Length。
Assign
//赋值运算,如 (a = b )。
Block
//表达式的块。
Call
//某个方法调用,如在 obj.sampleMethod ( ) 表达式。
Coalesce
//一个表示空合并操作,如节点 ( a ?? b) 在 C# 或 If(a, b) 在 Visual Basic 中。
Conditional
//条件运算,如 a > b? a : b 在 C# 或 If(a > b, a, b) 在 Visual Basic 中。
Constant
//常量的值。
Convert
//强制转换或转换操作中,如 ( SampleType)obj C# 中或 CType(obj, SampleType) 在 Visual Basic 中。 对于数值的转换,如果转换后的值对于目标类型来说太大不引发异常。
ConvertChecked
//强制转换或转换操作中,如 ( SampleType)obj C# 中或 CType(obj, SampleType) 在 Visual Basic 中。 对于数值的转换,如果转换后的值不符合目标类型是引发异常。
DebugInfo
//调试信息。
Decrement
//一元递减操作,如 ( a - 1) C# 和 Visual Basic 中。 该对象 a 不应就地修改。
Default
//默认值。
Divide
//除法运算,如 ( a / b), ,针对数值操作数。
DivideAssign
//除的复合赋值运算,如 ( a /= b), ,针对数值操作数。
Dynamic
//动态操作。
Equal
//一个表示相等比较,如节点 ( a == b) 在 C# 或 (a = b) 在 Visual Basic 中。
ExclusiveOr
//按位或逻辑 XOR 操作,如 ( a ^ b) 在 C# 或 (a Xor b) 在 Visual Basic 中。
ExclusiveOrAssign
//按位或逻辑 XOR 复合赋值运算,如 ( a ^= b) C# 中。
Extension
//扩展表达式。
Goto
//一个"转到"表达式,如 goto Label 在 C# 或 GoTo Label 在 Visual Basic 中。
GreaterThan
//"大于"比较,如 ( a > b)。
GreaterThanOrEqual
//"大于或等于"比较,如 ( a >= b)。
Increment
//一元递增操作,如 ( a + 1) C# 和 Visual Basic 中。 该对象 a 不应就地修改。
Index
//索引操作或访问不采用参数的属性的操作。
Invoke
//操作调用的委托或 lambda 表达式,如 sampleDelegate.Invoke ( )。
IsFalse
//一个 false 条件值。
IsTrue
//一个 true 条件值。
Label
//标签。
Lambda
//Lambda 表达式,如 a => a + a 在 C# 或 Function(a) a + a 在 Visual Basic 中。
LeftShift
//按位左移运算,如 ( a << b)。
LeftShiftAssign
//按位左移复合赋值运算,如 ( a <<= b)。
LessThan
//"小于"比较,如 ( a<b)。
LessThanOrEqual
//"小于或等于"比较,如 ( a <= b)。
ListInit
//创建一个新的操作的 IEnumerable 对象,并对其进行初始化从列表中的元素,如 new List<SampleType>(){ a, b, c } 在 C# 或 Dim sampleList = { a, b, c } 在 Visual Basic 中。
Loop
//一个循环,如 for 或 while。
MemberAccess
//从一个字段或属性,如读取操作 obj.SampleProperty。
MemberInit
//运算,创建一个新的对象并初始化一个或多个成员,如 new Point { X = 1, Y = 2 } 在 C# 或 New Point With {.X = 1, .Y = 2} 在 Visual Basic 中。
Modulo
//算术余数运算,如 ( a % b) 在 C# 或 (a Mod b) 在 Visual Basic 中。
ModuloAssign
//算术余数复合赋值运算,如 ( a %= b) C# 中。
Multiply
//乘法运算,如 ( a* b ), ,不进行溢出检查,针对数值操作数。
MultiplyAssign
//乘法复合赋值运算,如 ( a *= b), ,不进行溢出检查,针对数值操作数。
MultiplyAssignChecked
//乘法复合赋值运算,如 ( a *= b), ,,进行溢出检查,针对数值操作数。
MultiplyChecked
//乘法运算,如 ( a* b ), ,,进行溢出检查,针对数值操作数。
Negate
//算术求反运算,如 (-a)。 该对象 a 不应就地修改。
NegateChecked
//算术求反运算,如 (-a), ,,进行溢出检查。 该对象 a 不应就地修改。
New
//调用构造函数以创建新的对象,如操作 new SampleType()。
NewArrayBounds
//创建一个新数组,其中每个维度的下限指定,如操作 new SampleType[dim1, dim2] 在 C# 或 New SampleType(dim1, dim2) 在 Visual Basic 中。
NewArrayInit
//操作,创建一个新的一维数组并对其进行初始化从列表中的元素,如 new SampleType[]{a, b, c} 在 C# 或 New SampleType(){a, b, c} 在 Visual Basic 中。
Not
//按位求补或逻辑求反运算。 在 C# 中,则等同于 (~a) 整型和 (!a) 布尔值。 在 Visual Basic 中,则等同于 (Not a)。 该对象 a 不应就地修改。
NotEqual
//不相等比较,如 (a != b) 在 C# 或 (a <> b) 在 Visual Basic 中。
OnesComplement
//一个二进制反码运算,如 (~a) C# 中。
Or
//按位或逻辑 OR 操作,如 (a | b) 在 C# 或 (a Or b) 在 Visual Basic 中。
OrAssign
//按位或逻辑 OR 复合赋值运算,如 (a |= b) C# 中。
OrElse
//短路条件 OR 操作,如 (a || b) 在 C# 或 (a OrElse b) 在 Visual Basic 中。
Parameter
//对参数或变量的表达式的上下文中定义的引用。 有关更多信息,请参见ParameterExpression。
PostDecrementAssign
//一元后缀递减,如 (a--)。 该对象 a 应就地修改。
PostIncrementAssign
//一元后缀递增,如 (a++)。 该对象 a 应就地修改。
Power
//如引发数字进行幂运算的数学运算 (a ^ b) 在 Visual Basic 中。
PowerAssign
//如引发数字进行幂运算的复合赋值运算 (a ^= b) 在 Visual Basic 中。
PreDecrementAssign
//一元前缀递减,如 (--a)。 该对象 a 应就地修改。
PreIncrementAssign
//一元前缀递增,如 (++a)。 该对象 a 应就地修改。
Quote
//具有类型的常量值的表达式 Expression。 一个 Quote 节点可以包含对它所代表的表达式的上下文中定义的参数的引用。
RightShift
//按位右移运算,如 (a >> b)。
RightShiftAssign
//按位右移复合赋值运算,如 (a >>= b)。
RuntimeVariables
//运行时变量的列表。 有关详细信息,请参阅RuntimeVariablesExpression。
Subtract
//减法运算,如 (a - b), ,不进行溢出检查,针对数值操作数。
SubtractAssign
//减法复合赋值运算,如 (a -= b), ,不进行溢出检查,针对数值操作数。
SubtractAssignChecked
//减法复合赋值运算,如 (a -= b), ,,进行溢出检查,针对数值操作数。
SubtractChecked
//算术减法运算,如 (a - b), ,,进行溢出检查,针对数值操作数。
Switch
//一个切换操作,如 switch 在 C# 或 Select Case 在 Visual Basic 中。
Throw
//引发异常,如操作 throw new Exception()。
Try
//一个 try-catch 表达式。
TypeAs
//显式引用或装箱转换在其中 null 如果转换失败,如提供 (obj as SampleType) 在 C# 或 TryCast(obj, SampleType) 在 Visual Basic 中。
TypeEqual
//确切类型测试。
TypeIs
//一种类型测试,如 obj is SampleType 在 C# 或 TypeOf obj is SampleType 在 Visual Basic 中。
UnaryPlus
//一元正运算,如 (+a)。 预定义的一元正运算的结果是操作数的值,但用户定义的实现可能有不寻常的结果。
Unbox
//取消装箱值类型的操作,如 unbox 和 unbox.any MSIL 中的说明。