[Expression Tree] MSDN學習系列 1-運算式樹狀架構

    最近參考一些較知名的Library,例:Automapper、DynamicQuery..等等,都有用到大量的Expression來作動態查詢,由於Expression認識的不夠深,以致在擷取某些程式碼來修改時都覺得很吃力......=.=。而這兩天剛好看到msdn有提供一系列的文章,想說將它整個練習一遍,並將所理解的記錄下來 ps:其實是轉化自己較容易懂的語言XD。

什麼是Expression Tree?


就是將程式碼當作樹狀結構的資料,並可以動態修改Runtime的程式碼、動態查詢及取得程式碼中的相關資訊。

練習第一個Expresssion Tree


以下為Lambda運算式並宣告一個泛型委派的變數來接,表示為傳入一個num參數,並回傳布林值,在執行只要視為一個方法來叫用即可lambda(100)
 Func<int, bool> lambda = num => num < 5;
同樣的Lambda運算式!若宣告一個Expression變數來接,則可以取出此運算式的相關資訊,如下取出參數、常數、二元運算式等資訊,最後再透過compiler方法建置成泛型委派來執行。
   Expression<Func<int, bool>> lambda = num => num > 10;
Console.WriteLine(lambda.Body);//運算式主體 n*n
Console.WriteLine(lambda.Parameters[0]);//參數
Console.WriteLine(lambda.NodeType);//節點型別
Console.WriteLine(lambda.ReturnType);//回傳型別
Console.WriteLine(lambda.Compile()(10));//建置後執行

顯示結果
image

透過Expression API動態建立一個簡單運算式


 

在上面例子是將已建立的Lambda運算式去透過Expression去取得,而我們也可以透過Expression所以提供的工廠方法去建立一個Lambda運算式來使用
      ParameterExpression num = Expression.Parameter(typeof(int), "n");
Expression<Func<int, int>> expr =
Expression<Func<int, int>>.Lambda<Func<int, int>>(//建立Lambda方法
Expression.Multiply(num, num)//建立一個num*num的主體
, new ParameterExpression[] { num }//此主體方法為傳入一個num參數
);
Console.WriteLine(expr.Body);
Console.WriteLine(expr.Parameters[0]);
Console.WriteLine(expr.NodeType);
Console.WriteLine(expr.ReturnType);
Console.WriteLine(expr.Compile()(10));

顯示結果
image


透過Expression API動態建立判斷式、迴圈區塊的運算式

   ParameterExpression value = Expression.Parameter(typeof(int), "value");
ParameterExpression result = Expression.Parameter(typeof(int), "result");
LabelTarget label = Expression.Label(typeof(int));
BlockExpression block = Expression.Block(//建立方法區塊
new[] { result },//指定方法的參數為result
Expression.Assign(result, Expression.Constant(1)),//設定變數result=1
Expression.Loop(//建立Loop迴圈區塊
Expression.IfThenElse(//建立判斷式區塊
Expression.GreaterThan(value, Expression.Constant(1)),//條件:參數大於5
Expression.MultiplyAssign(result, Expression.PostDecrementAssign(value)),//true:執行乘法運算並將結果給result 例:result*=value
Expression.Break(label, result)//false:跳離Loop並到下方的label
)
, label//指定標籤,讓上方的Break跳到此行用
)
);
var lamba = Expression.Lambda<Func<int, int>>(block, value);//將方法區塊指定成Lamdba運算式
int factorial = lamba.Compile()(5);
Console.WriteLine(lamba);
Console.WriteLine(factorial);


輸出結果:120


使用DebugView查看ExpressionTree的內容執行結果


在撰寫Expression時若不確定寫的是否正確,可以在Debug時查看展開後的運算式內容
image

image



參考資料


http://msdn.microsoft.com/zh-tw/library/bb397951.aspx
第二篇....待續

這個網誌中的熱門文章

[.NET Core] 將專案發行至IIS

[TFS] 分支與合併