表達式樹(Expression Tree)html
表達式樹是不可執行的代碼,它只是用於表示一種樹狀的數據結構,樹上的每個節點都表示爲某種表達式類型,大概有25種表達式類型,它們都派生自Expression類。建立表達式樹具體有兩個優點:數據庫
1.對錶達式樹的代碼進行編輯修改,使表達式樹中的代碼變成動態代碼,根據不一樣的數據庫修改樹上的代碼邏輯從而達到動態切換數據庫查詢語句的目的,用表達式樹能夠動態構建針對不一樣數據庫的查詢語句。express
2.完成相似反射訪問未知對象的屬性,經過動態構造表達式樹,生成委託。 數組
三種方式建立表達式樹數據結構
Expression(表達式類)ide
此類能夠稱爲表示表達式的類型,或也稱爲一棵表達式樹。由於當你每建立一個表示表達式的實例時,均可以將該類型實例當作是一棵表達式樹。每種表示表達式的類型都有一個具體的類型,如Expression的Variable()方法建立的是ParameterExpression類型的表達式,Expression的Add()方法建立的則是BinaryExpression類型的表達式。不管哪一種表示表達式的類型都是從Expression派生。函數
//使用Expression的靜態方法建立表達式
ParameterExpression variable = Expression.Variable ( typeof ( int ) , "x" ); // x
LambdaExpression(Lambda表達式類)oop
LambdaExpression是Expression的子類,此類型表示一個Lambda表達式類型。post
//使用Expression的靜態方法建立表達式
LambdaExpression lambda = Expression.Lambda ( Expression.Variable ( typeof ( int ) , "x" ) ); //()=>x
Expression<TDelegate>(表達式委託類)學習
此類從LambdaExpression派生,因此也可使用此類來建立一個表示Lambda表達式的類型,這種方式只能提供一個Lambda表達式,不能提供Lambda語句。
//直接將Lambda表達式表示爲一個LambdaExpression
Expression<Func<int,int>> Info = ( x ) => x; //linq查詢方法中的參數就是相似於這種將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 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
//加法運算,如 ( 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 中的說明。
/*節點的NodeType用以描述表達式的行爲*/
表達式類型的轉換
全部表達式類型都從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;
Func<int , int , bool> funcDeleg = additionExpressionInfo.Compile ( );
Console.WriteLine(funcDeleg ( 1 , 2 ) ); //print true
//或
bool IsTrue = additionExpressionInfo.Compile ( ) ( 1 , 2 ); // true
Expression的靜態方法
Constant ( )
//建立一個常量表達式,注:Expression會自動生成常量的名字
//示例:
//建立一個表示常量的表達式:string x="寒食"
Expression.Constant( "寒食",typeof(string) )
Variable ( )
//建立一個ParameterExpression表示變量/參數變量表達式
Parameter ( )
//建立一個ParameterExpression表示變量/參數變量表達式
//示例:
Expression.Variable( typeof (int ) , "x" ); /*等同於*/ Expression.Parameter( typeof (int ) , "y" );
PostIncrementAssign ( )
//建立一個UnaryExpression表示++後置,相似的有PostDecrementAssign表示--後置
PreIncrementAssign ( )
//建立一個UnaryExpression表示++前置,相似的有PreDecrementAssign表示--前置
Assign ( )
//建立一個BinaryExpression表示賦值表達式,賦值表達式老是有左右兩邊的操做數
//示例:
BinaryExpression binaryExpression = Expression.Assign ( Expression.Variable ( typeof ( int ) , "x" ) , Expression.Constant ( 2 ) ); //左是x,右是2 int x=2
Console.WriteLine(binaryExpression.ToString ( ) ); //print int x=2
Add ( )
//建立一個BinaryExpression表示加法運算,表示左邊操做數+右邊操做數。相似的有AddAssign ( )表示左邊操做數=左邊操做數+右邊操做數
//相似的有:Subtract(減)、Divide(除法)、Power(求冪)、Modulo(求餘),也都有相似的加上Assign後綴的方法
//示例:
var s = Expression.Add ( Expression.Parameter ( typeof ( int ) , "x" ) , Expression.Constant ( 1 ) ).ToString ( ); //x+1
Console.WriteLine(s ); //x+1
var s2 = Expression.AddAssign ( Expression.Parameter ( typeof ( int ) , "x" ) , Expression.Constant ( 1 ) ).ToString ( );
Console.WriteLine(s2 );//x+=1
Lambda ( )
//建立一個LambdaExpression表示Lambda表達式
Lambda<TDelegate>(Expression runExpr, Expression runExprParameter)
//建立一個Expression<TDelagate>實例表示Lambda表達式,與非泛型版本的Lambda()方法相比,此方法顯示指定了與Lambda對應的委託,執行Lambda時輸入、輸出參數會一目瞭然
//runExpr :表示被封裝到Lambda的表達式,runExprParameter :Lambda接收的參數表達式
Call ( Expression expression , MethodInfo method , params Expression [ ] methodParamters )
//建立一個MethodCallExpression表示調用某個方法的表達式,只有表示方法調用的表達式和塊表達式能夠執行
//方法調用有兩種狀況:1.對象調用方法 2.類型調用方法 好比:Animal a=new Animal(); a.Show()區別於Animal.Count()
//若是不是對象調用方法則第一個參數可提供null,不然第一個參數須要提供調用方法的對象,對象也必須是一個Expression
//示例:
//假設要爲這段代碼建立表達式樹:Console.WriteLine( ),
MethodCallExpression method = Expression.Call (
null , //無實例調用方法
typeof ( Console ).GetMethod ( "WriteLine" , new Type [ ] { typeof ( string ) } ) , //方法調用的表達式
Expression.Constant ( "寒食" , typeof ( string ) ) //方法的參數
);
Expression<Action> action = Expression.Lambda<Action> ( method );
action.Compile( ) ( ); // print 寒食
//假設要爲這段代碼建立表達式樹:"hello world".ToUpper(),
Expression callExpr = Expression.Call (
Expression.Constant ( "hello world" ) , //有實例調用方法
typeof ( string ).GetMethod ( "ToUpper" , new Type [ ] { } )
);
string newStr = Expression.Lambda<Func<string>> ( callExpr ).Compile ( ) ( ); // HELLO WORLD
Block ( )
//建立一個BlockExpression表示塊表達式,此方法的最後一個參數表達式的值將會自動做爲返回的結果
//示例:塊中的表達式都是一步一步的定義出來的,建立塊表達式時你能夠想象一下在塊中寫C#代碼塊的流程,這樣你就知道下面Block ( )方法的參數(表達式)是如何建立的了
ParameterExpression x = Expression.Parameter ( typeof ( int ) , "x" ); // int x
ParameterExpression y = Expression.Parameter ( typeof ( int ) , "y" ); // int y
BlockExpression block = Expression.Block (
new ParameterExpression [ ] { x , y } , // int x,int y 定義塊做用域中的變量表達式
Expression.Assign ( x , Expression.Constant ( 100 ) ) , //x=100 定義塊做用域中的賦值表達式
Expression.Assign ( y , Expression.Constant ( 200 ) ) , //y =200 定義塊做用域中的賦值表達式
Expression.AddAssign ( x , y ) // var r = x + y ,r將自動做爲塊的返回結果
);
Func<int> func = Expression.Lambda<Func<int>> ( block ).Compile ( );
Console.WriteLine(func ( ) ); // print 300
//BlockExpression是一個塊語句,至關於函數,
//若是塊要接收參數,好比外部調用時傳遞實參,則不要在塊中使用new ParameterExpression[ ] { } 聲明同名的變量表達式,不然會覆蓋掉參數
//示例:
var name = Expression.Parameter ( typeof ( string ) , "name" );
var function = Expression.Block (
new ParameterExpression [ ] { name } , //覆蓋掉了塊的參數name
name //返回的是塊內部定義的name表達式
);
string s = Expression.Lambda<Func<string , string>> ( function , name ).Compile ( ) ( "sam" );
Console.WriteLine(s ); // print ""
var name = Expression.Parameter ( typeof ( string ) , "name" );
var function = Expression.Block (
name
);
string s = Expression.Lambda<Func<string , string>> ( function , name ).Compile ( ) ( "sam" );
Console.WriteLine(s ); // print sam
LessThanOrEqual
//建立一個BinaryExpression表示<=的表達式,相似的有LessThan ( )
GreaterThanOrEqual ( )
//建立一個BinaryExpression表示>=的表達式,相似的有GreaterThan ( )
//示例:
var x = Expression.Parameter ( typeof ( int ) , "x" );
var y = Expression.Parameter ( typeof ( int ) , "y" );
var block = Expression.Block (
new ParameterExpression [ ] { x , y } ,
Expression.Assign ( x , Expression.Constant ( 100000 ) ) ,
Expression.Assign ( y , Expression.Constant ( 200 ) ) ,
Expression.LessThanOrEqual ( x , y ) // x >= y
);
bool IsTrue = Expression.Lambda<Func<bool>> ( block ).Compile ( ) ( );
Console.WriteLine(IsTrue ); // print true
IfThenElse ( Expression expressionForTest , Expression ifTestIsTrue , Expression ifTestIsFlase )
//建立一個ConditionalExpression表示條件語句表達式
//示例:
var x = Expression.Parameter ( typeof ( int ) , "x" );
var y = Expression.Parameter ( typeof ( int ) , "y" );
var block = Expression.Block (
new ParameterExpression [ ] { x , y } ,
Expression.Assign ( x , Expression.Constant ( 100000 ) ) ,
Expression.Assign ( y , Expression.Constant ( 200 ) ) ,
Expression.IfThenElse (
Expression.GreaterThanOrEqual ( x , y ) , // if ( x >= y )
Expression.Call ( null , typeof ( Console ).GetMethod ( "WriteLine" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "x>y==true" ) ) , //條件爲真時執行
Expression.Call ( null , typeof ( Console ).GetMethod ( "WriteLine" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "x>y==false" ) ) //條件爲假時執行
)
);
Expression.Lambda<Action>(block ).Compile ( ) ( ); // print x>y==true
Label ( Type type )
//建立一個LabelTarget表示標籤,此標籤經常使用於退出語句塊的標誌,將標籤做爲Loop ( )方法的最後一個參數,而後在某個條件表達式中使用Expression.Break(LabelTarget )退出循環
//此方法可接收一個可選的Type類型的參數,在循環中假設循環退出時能夠將某個值輸出到標籤中,這樣你能夠在外部拿到這個值
//參看下面的Loop語句表達式的示例,示例使用了Label ( )方法建立標籤
Label ( LabelTarget label , ParameterExpression defaultValue )
//建立一個LabelExpression表示與LabelTarget關聯的終止執行表達式,經常使用語在塊中結合Expression.Return()方法使用
//參看下面的Return語句表達式的示例,示例使用了Label ( )方法建立標籤
Break ( )
//建立一個GotoExpression表示退出循環,若是有嵌套循環,嵌套的循環內也得使用此方法來退出嵌套循環
//此方法可接收一個LabelTarget和一個可選的值參,退出循環時,值參會賦給標籤,以便拿到這個值
Return ( )
//建立一個GotoExpression表示退出循環、退出方法體、退出塊
//示例:
ParameterExpression x = Expression.Parameter ( typeof ( int ) );
LabelTarget label = Expression.Label ( typeof ( int ) );
BlockExpression block = Expression.Block (
//若是x==1
Expression.IfThen (
Expression.Equal ( x , Expression.Constant ( 1 ) ) ,
Expression.Block (
Expression.Assign ( x , Expression.Constant ( 100 ) ) ,
Expression.Return ( label , x ) //直接跳轉到與label標籤關聯的LabelExpression表達式
)
) ,
//若是x==2
Expression.IfThen (
Expression.Equal ( x , Expression.Constant ( 2 ) ) ,
Expression.Block (
Expression.Assign ( x , Expression.Constant ( 200 ) ) ,
Expression.Return ( label , x ) //直接跳轉到與label標籤關聯的LabelExpression表達式
)
) ,
//與label標籤關聯的LabelExpression表達式,
//不管兩個IfThen表達式執行與否,此LabelExpressio始終會執行,
//若是是這樣,那麼label就沒有默認值,因此須要爲label提供一個默認值Expression.Constant(300)
Expression.Label ( label , Expression.Constant ( 300 ) )
);
int r = Expression.Lambda<Func<int , int>> ( block , x ).Compile ( ) ( 2 ); //Lambda方法的參數2表示調用委託時傳遞的參數
Console.WriteLine(r ); //print 200
Loop ( )
//建立一個LoopExpression表示循環語句表達式
//示例:
var label = Expression.Label ( typeof ( int ) );
var x = Expression.Variable ( typeof ( int ) , "x" );
var block = Expression.Block (
new [ ] { x } ,
Expression.Assign ( x , Expression.Constant ( 0 ) ) ,
Expression.Loop (
Expression.IfThenElse (
Expression.LessThan (
x ,
Expression.Constant ( 10 )
) ,
Expression.PostIncrementAssign ( x ) ,// x++
Expression.Break ( label , x ) //將x做爲標籤的值
) ,
label
)
);
int r = Expression.Lambda<Func<int>> ( block ).Compile ( ) ( );
Console.Write(r ); // print 10
PropertyOrField ( )
//建立一個MemberExpression表示訪問成員字段或屬性的表達式,相似的有Field( )、Property( )方法
//示例:
Animal horse = new Animal { Name = "sam" };
MemberExpression member = Expression.PropertyOrField ( Expression.Constant ( horse ) , "Name" );
Console.WriteLine(member.ToString());
string Name = Expression.Lambda<Func<string>> ( member ).Compile ( ) ( );
Console.WriteLine(Name ); // print sam
Invoke ( )
//建立一個InvocationExpression表示執行Lambda並傳遞實參的表達式
//示例:
Expression<Func<int , int , bool>> InvocationInfo = ( i , ii ) => ( i + ii ) > 1;
InvocationExpression invocationExpression = Expression.Invoke (
InvocationInfo , //函數
Expression.Constant ( 1 ) , //參數
Expression.Constant ( 2 ) //參數
);
Console.WriteLine(invocationExpression.ToString ( ) ); // Invoke ( (i, ii) => ( (i + ii) > 3 ), 1, 2 )
bool r = Expression.Lambda<Func<bool>> ( invocationExpression ).Compile ( ) ( );
Console.WriteLine(r ); // print true
ElementInit ( MethodInfo addMethod , Expression expr )
//建立一個ElementInit表示調用集合的Add方法添加元素
//示例:參看下面的ListInit()方法
New ( )
//建立一個NewExpression表示調用無參構造函數
//示例:參看下面的ListInit()方法
ListInit ( )
//建立一個ListInitExpression表示集合初始化器
//示例:
string proner1 = "sam";
string proner2 = "korn";
MethodInfo add = typeof ( List<string> ).GetMethod ( "Add" );
//生成表達式:Add("sam")
var elm1 = Expression.ElementInit (
add ,
Expression.Constant ( proner1 )
);
//生成表達式:Add("korn")
var elm2 = Expression.ElementInit (
add ,
Expression.Constant ( proner2 )
);
//生成表達式:new List<string>( )
var list = Expression.New ( typeof ( List<string> ) );
//生成表達式:new List<string> { "sam" , "korn" }
var listObject = Expression.ListInit (
list ,
elm1 ,
elm2
);
Console.WriteLine(listObject.ToString() );
Quote ( )
//將LambdaExpression包裝爲一個常量
//示例:
var add = Expression.Add ( Expression.Constant ( 1 ) , Expression.Constant ( 1 ) );
var expr1 = Expression.Lambda<Func<int>> ( add );
var expr2 = Expression.Quote ( Expression.Lambda ( add ) );
Console.WriteLine(expr1.Compile ( ) ( ) ); //print 2
Console.WriteLine(expr2 ); // print ()=>1+1
通過以上的逐步分析,如今能夠建立一個稍微複雜一點的例子了,下面演示了建立一個塊語句表達式,在塊中建立了雙重循環表達式,經過調用Lambda方法提取一個委託來執行表達式樹:
LabelTarget outerBreak = Expression.Label ( );
LabelTarget innerBreak = Expression.Label ( );
var x = Expression.Variable ( typeof ( int ) , "x" );
var y = Expression.Variable ( typeof ( int ) , "y" );
var result = Expression.Variable ( typeof ( int ) , "result" );
var block = Expression.Block (
new [ ] { x } ,
Expression.Assign ( x , Expression.Constant ( 1 ) ) ,
//循環
Expression.Loop (
//條件判斷
Expression.IfThenElse (
//若是表達式爲真
Expression.LessThan ( x , Expression.Constant ( 10 ) ) , // if x<10
//爲真時執行
Expression.Block (
new [ ] { y } ,
Expression.Assign ( y , Expression.Constant ( 1 ) ) ,
//內層循環
Expression.Loop (
Expression.IfThenElse (
Expression.LessThanOrEqual ( y , x ) , // if y <= x
//爲真時執行
Expression.Block (
new [ ] { result } ,
Expression.Assign ( result , Expression.Multiply ( x , y ) ) ,
Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( int ) } ) , y ) ,
Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "×" ) ) ,
Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( int ) } ) , x ) ,
Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "=" ) ) ,
Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( int ) } ) , result ) ,
Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "\t" ) ) ,
Expression.PostIncrementAssign ( y ) // y++
) ,
//爲假時退出內層循環
Expression.Break ( innerBreak )
) ,
innerBreak
) ,//內層循環end
Expression.Call ( null , typeof ( Console ).GetMethod ( "WriteLine" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "" ) ) ,
Expression.PostIncrementAssign ( x ) // x++
) ,
//爲假時執行
Expression.Break ( outerBreak )
)
, outerBreak )
);
Expression.Lambda<Action> ( block ).Compile ( ) ( );

Linq查詢中的表達式樹
爲何有時候Linq查詢須要表達式樹?
Linq To SQL
Queryable類爲IQueryable<T>實現了一系列的擴展方法用於Linq To SQL的查詢,這些擴展方法大部分都返回一個IQueryable<T>類型,IQueryable <T>有一個Expression類型的屬性叫作Expression,當使用Linq To SQL執行Linq查詢時會調用那些擴展方法,而擴展方法都要求一個Expression<T>類型的參數,擴展方法接收這個做爲參數的表達式樹後將它做爲IQueryable <T>集合的Expression屬性來使用,IQueryable <T>還有一個叫作IQueryProvider(Linq查詢提供程序)的屬性,IQueryProvider類拿到Expression表達式樹後會對其進行解析以便生成純SQL語句,再將SQL字符串發送到數據庫以便執行。因此今後處能夠看出來,表達式樹具備很高的靈活性,它並不是可執行的C#代碼,但經過Expression提供的一系列屬性和方法,使用ExpressionVisitor(對錶達式樹進行訪問和修改的類)你能夠對其進行解析,根據需求的不一樣從而動態修改表達式樹中的表達式以便生成可在非C#環境執行的代碼。
Linq To Object
Enumerable類爲IEnumerable<T>實現了一系列的擴展方法用於Linq To Object的查詢,這些擴展方法大部分都返回一個IEnumerable<T>類型,IEnumerable<T>沒有須要接收表達式樹做爲參數的擴展方法,由於Linq To Object是直接查詢內存中的數據,不須要轉化爲SQL語句,因此根本用不上表達式樹。
C# - 學習總目錄
參考:http://www.infoq.com/cn/articles/dot-net-expression-tree