表达式树(ExpressionTree)
表达式树是不可执⾏的代码,它只是⽤于表⽰⼀种树状的数据结构,树上的每⼀个节点都表⽰为某种表达式类型,⼤概有25种表达式类型,它们都派⽣⾃Expression类。创建表达式树具体有两个优势:
1.对表达式树的代码进⾏编辑修改,使表达式树中的代码变成动态代码,根据不同的数据库修改树上的代码逻辑从⽽达到动态切换数据库查询语句的⽬的,⽤表达式树可以动态构建针对不同数据库的查询语句。
2.完成类似反射访问未知对象的属性,通过动态构造表达式树,⽣成委托。
三种⽅式创建表达式树
Expression(表达式类)
此类可以称为表⽰表达式的类型,或也称为⼀棵表达式树。因为当你每创建⼀个表⽰表达式的实例时,都可以将该类型实例看成是⼀棵表达式树。每种表⽰表达式的类型都有⼀个具体的类型,如Expression的Variable()⽅法创建的是ParameterExpression类型的表达式,Expression的Add()⽅法创建的则是BinaryExpression 类型的表达式。⽆论哪种表⽰表达式的类型都是从Expression派⽣。
//使⽤Expression的静态⽅法创建表达式
ParameterExpression variable = Expression.Variable ( typeof ( int ) , "x" );
LambdaExpression(Lambda表达式类)
LambdaExpression是Expression的⼦类,此类型表⽰⼀个Lambda表达式类型。
//使⽤Expression的静态⽅法创建表达式
LambdaExpression lambda = Expression.Lambda ( Expression.Variable ( typeof ( int ) , "x" ) );
Expression<TDelegate>(表达式委托类)
此类从LambdaExpression派⽣,所以也可以使⽤此类来创建⼀个表⽰Lambda表达式的类型,这种⽅式只能提供⼀个Lambda表达式,不能提供Lambda语句。
//直接将Lambda表达式表⽰为⼀个 LambdaExpression
Expression<Func<int,int>> Info = ( x ) => x;
执⾏表达式
假设创建了⼀个表⽰加法运算的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 x=Expression.Parameter ( typeof ( int ) , "x" ); //表⽰定义参数的Expression表达式
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类型的变量,其树结构如下:
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
上⾯例⼦中创建了⼀个表达式( x , y ) => x != y && x!=0,代码体是x != y && x!=0,&&是⼀个表达式,它含有左右两个操作数的⼦表达式,所以它会被拆分,左边x != y是⼀个表达式,右边 x!=0也是⼀个表达式,左右两边都含有⼦表达式,所以会继续拆分,直到⽆法拆分为⽌,结构如下:
Expression的⼦类
树的每个⼦节点是⼀个具体的表达式,它们都有⾃⼰的表达式类型,这些类型从Expression派⽣。
UnaryExpression; //⼀元运算表达式
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属性,此属性是⼀个枚举数,⽤以获取表达式属于什么种类(根据其⾏为判定)。
Add
//加法运算,如 a + b, ,不进⾏溢出检查,针对数值操作数。
AddAssign
//加法复合赋值运算,如 ( a += b), ,不进⾏溢出检查,针对数值操作数。
AddAssignChecked
//加法复合赋值运算,如 ( a += b), ,进⾏溢出检查,针对数值操作数。
AddChecked
variable used in lambda//加法运算,如 ( 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 中的说明。
表达式类型的转换
所有表达式类型都从Expression类派⽣,当创建⼀棵表达式树时,如果创建的是⼀个Lambda表达式,那么得到的是⼀个Expression<TDelegate>实
例,Expression<TDelegate>的Body属性存储Lambda封装的表达式,⽐如Expression<Func<int,int>> info=(x)=>x,其Body返回x,x是⼀个ParameterExpression,但Body被表⽰为表达式基类Expression,其实际存储的是ParameterExpression:
Expression<Func<int , int>> ExpressionInfo = ( x ) => x; // => x 被解析为ParameterExpression
Console.WriteLine ( ExpressionInfo.Body.GetType().Name ); //ParameterExpression
var pa = ExpressionInfo.Body as ParameterExpression; //可以将⽗类转⼦类,因为Body虽然是Expression类型,但实际存储的是⼦类实例
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
Console.WriteLine ( additionExpressionInfo.Body.GetType().Name ); //BinaryExpression
var add = additionExpressionInfo.Body as BinaryExpression; //可以将⽗类转⼦类,因为Body虽然是Expression类型,但实际存储的是⼦类实例
何时使⽤Expression<TDelegate>?
现在假设你要创建⼀个表⽰调⽤string的EndWith()⽅法的表达式,如果使⽤Expression的静态⽅法,你得创建⼀堆⼦表达式,然后将它们合成为⼀个表⽰⽅法调⽤的表达式,再将⽅法表达式、⽅法需要的参数表达式全部封装到LambdaExpression中,以便可以执⾏
var x = Expression.Parameter ( typeof ( string ) , "x" );
var y = Expression.Parameter ( typeof ( string ) , "y" );
var methodInfo = typeof ( string ).GetMethod ( "StartsWith" , new Type [ ] { typeof ( string ) } );
var call = Expression.Call ( x , methodInfo , y );
var lambda = Expression.Lambda<Func<string , string , bool>> ( call , new [ ] { x , y } );
Console.WriteLine ( lambda.ToString ( ) ); // (x,y)=>x.StartsWith(y)
bool IsTrue = lambda.Compile ( ) ( "ABC" , "A" );
Console.WriteLine ( IsTrue ); //print true
⼀个简单的⽅法调⽤需要写出⼀堆臃肿难堪的代码?像这样简单的运算完全可以直接使⽤Expression<TDelegate>类型,因为你只需要创建⼀个Lambda表达式,⼀个Lambda表达式本⾝就可以被看成是⼀个Expression<TDelegate>,这样你完全不需要使⽤Expression的静态⽅法创建那么多表达式,⼀切交给Lambda表达式即可:
//产⽣与上⾯代码完全⼀样的表达式树
Expression<Func<string , string , bool>> lambdaExpr = ( str1 , str2 ) => str1.StartsWith ( str2 );
IsTrue=lambdaExpr.Compile ( ) ( "ABC" , "A" );
Console.WriteLine ( IsTrue ); //print true
前⾯说过Expression<TDelegate>只能表⽰⼀个Lambda表达式,不能表⽰Lambda语句。也即类似⼀元、⼆元运算的表达式完全可以使⽤Lambda来创建⼀个Expression<TDelegate>,⽽像条件判断、循环等语句表达式就不能使⽤Lambda表达式来创建,它们只能是Lambda语句,⽽Lambda语句不被视为
Expression<TDelegate>,此时才需要考虑使⽤Expression的静态⽅法来构造更复杂的表达式逻辑。
LambdaExpression的⽅法
Body
//获取Lambda的⽅法体所表⽰的表达式
Parameters
//获取Lambda表达式的参数,返回⼀个ReadOnlyCollection<ParameterExpression> 集合,该集合存储了每⼀个参数变量表达式,可通过索引对项进⾏检索
//⽰例:
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
foreach (var pa in additionExpressionInfo.Parameters )
{
Console.WriteLine(pa.Name );
}
NodeType
//节点的类型,⼀个ExpressionType枚举数,⽤来描述表达式的⾏为
ReturnType
//Lambda表达式的返回类型
Type
//返回Expression<TDelegate>类型,
/
/⽰例:
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
Console.WriteLine(expressionInfo.Type ); //print System.Func`2[System.Int32,System.Int32]
Compile ( )
//将LambdaExpression封装的表达式⽣成委托返回
//⽰例:
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论