上回
我們說到手工解析expression tree,以便獲得其中的邏輯或者執行我們自定義的語義動作。這種做法擴充套件了c#語言的威力,讓我們可以用c#的語法來做更多的事情,例如linq to sql。今天我們要學習一種相反的做法,手工建立表示式樹,然後讓.net來解析它。這是一種強大的動態程式設計手段。我們可以用它來完成許多以前需要reflection.emit才能完成的任務。
這裡我們仍要使用諸多表示式中與眾不同的lambdaexpression。在visual studio中我們可以看到他有乙個compile方法。這是做什麼的呢。在第一篇
中我們了解到lambda表示式表達的是乙個方法。那麼lambdaexpression.compile方法自然就是將lambda表示式的表示式樹真的編譯成乙個.net方法。我們看看這段**:
parameterexpression pi = expression.parameter(typeof(int), "i");lambdaexpression fexp =
expression.lambda(
expression.add(pi, expression.constant(1))
, pi);
delegate f = fexp.compile();
已經學過第一篇的同學一眼就能看出,這個表示式樹其實就是 i => i + 1。後面的compile方法生成了乙個delegate。如果我們試著獲取這個delegate的型別會發現他是func!實際上,這個委託所引用的方法是乙個dynamicmethod編譯後的結果。dynamicmethod是reflection.emit的強大功能,可在執行時動態建立出.net方法來。我們可以正常地呼叫這個委託。
expression.lambda這個方法還有乙個泛型版過載,它可以建立如同c#自己生成一樣的強型別lambdaexpression。強型別lambdaexpression的compile就更好了,能直接生成強型別的委託。這樣我們呼叫起來就更愉快了:
parameterexpression pi = expression.parameter(typeof(int), "i");var fexp =
expression.lambda>(
expression.add(pi, expression.constant(1))
, pi);
var f = fexp.compile();
console.writeline(f(3));
如您所預料的一樣,輸出是「4」。
有人可能要問了,我們幹嘛不直接expression> f = i => i + 1; 呢?從這個例子我們的確看不出什麼優勢。但是別忘了前面的expression構造過程是完全可以用**控制的,可以隨意加入任何動態的邏輯。下面我們就來挑戰乙個用靜態**無法做到的任務:編寫乙個可以響應任何事件的響應函式。我們知道事件的響應函式大都是乙個sender引數,乙個eventargs或其某子類的引數。但這只是.net類庫約定的常見用法,clr完全沒有規定event的響應函式應該有幾個引數,有沒有返回值。假設沒有ref或者out這樣的引數(很少用於event中),我們想用這樣乙個方法充當任何乙個事件的響應函式:
static object generalhandler(params object args)
但是,真正想要作為事件響應函式的那個方法必須真的是事件所需的委託型別。我們這樣乙個params引數的方法顯然不能滿足各色事件所需的獨特委託。比如說我們要把這個事件掛在button.click事件上,而button.click事件想要的是這樣乙個委託:
public delegate voidroutedeventhandler(object sender, routedeventargs e);
別的事件可能還需要別的委託。我們的任務就是,在執行時動態建立這一委託所需要的方法,然後呼叫剛才我們定義的通用響應函式,完成響應函式的掛接。這個委託的型別可以從事件的eventhandlertype中取得,然後我們再取得委託的invoke方法即可獲取它所有的引數。
接下來是關鍵步驟,我們希望動態建立出這樣乙個方法來:
//假設這是動態建立出來的乙個方法static void routedeventhanler_dynamic(object sender, routedeventargs e)
這裡面有乙個呼叫params型引數的方法呼叫。在expressiontree裡沒有直接對應於這種呼叫的節點型別。我們必須還原成它編譯後的底層模樣,展開params引數隱式生成的陣列:
//假設這是動態建立出來的乙個方法static void routedeventhanler_dynamic(object sender, routedeventargs e)
);}
現在我們可以展示動態建立這一表示式樹的完整**了:(請**注釋了解**)
public static classgeneraleventhandling
public static void attachgeneralhandler(object target, eventinfo targetevent)
//呼叫我們的generalhandler
methodinfo executemethod = typeof(generaleventhandling).getmethod(
"generalhandler", bindingflags.static | bindingflags.nonpublic);
expression lambdabodyexp =
expression.call(null, executemethod, expression.newarrayinit(typeof(object), argsarrayexp));
//如果有返回值,那麼將返回值轉換成委託要求的型別
//如果沒有返回值就這樣擱那裡就成了
if (!invokemethod.returntype.equals(typeof(void)))
//組裝到一起
lambdaexpression dynamicdelegateexp = expression.lambda(delegatetype, lambdabodyexp, param***p);
//我們建立的expression是這樣的乙個函式:
//(委託的引數們) => generalhandler(new object )
//編譯
delegate dynamicedelegate = dynamicdelegateexp.compile();
//完成!
targetevent.addeventhandler(target, dynamicedelegate);
}}
如果是在乙個wpf程式裡,並且有乙個button1的話,就可以用下列語法為button1的click事件繫結乙個我們通用的響應函式:
generaleventhandling.attachgeneralhandler(button1, button1.gettype().getevent("click"));
不僅僅click,用這個語法可以放心地為任何乙個.net型別的事件繫結引數(只要沒有使用ref或者out引數)。而且這裡面的反射和emit生成**僅需要執行一次,就不會再執行了,所以即使事件多次引發也不會有效能問題。你可以充分地在generalhandler方法中加入自己的邏輯。
至於這個能做什麼用,那就可以發揮你的想象力了。例如你可以在這個方法的幫助下實現乙個跨程序的event響應機制,就像remoting做的那樣。
目前,expression tree所能表達的邏輯還比較有限,尚不能完全替代reflection.emit。不過請放心,有lambdaexpression在,整個expression tree系統就是圖靈完備的。理論上所有演算法您都可以用expression tree寫出來:d .net framework 4的expression tree有了更多的進化,它可以大大強化expression tree在這個領域的應用。敬請期待後續篇章。
日常應用中我們常常會和一些資料實體類打交道。現在假設我們的資料實體類裡面定義了許多public的屬性。請用expression tree實現乙個動態邏輯,可以更新任意一種資料實體類的集合,比如list, list等等中每乙個元素的指定屬性。
比如我要對乙個集合中所有book元素的author屬性進行更新,您的程式就可以這樣呼叫:
listbooks = …
setallproperty(books, 「author」, 「ninputer」);
當然您寫的方法不知道book型別,也不知道我要更新哪個屬性,它應該能動態地支援任意的實體類集合。
(待續)
ExpressionTree學習筆記
這段時間需要制定自定義查詢條件,感覺有必要學習expressiontree。學習參考資料 public class people public string email public int cityid listpeople new list,new people new people new p...
重新上本科 堆排序 上
堆排序很有意思。作為排序演算法來講,它和快速排序都是o nlogn 的時間複雜度,都是就地排序,都是採用遞迴。兩者差不多,既生瑜,何生亮?不過堆是一種很有用的資料結構,通過堆排序演算法,可以學習堆這種資料結構,以後可能用得上。堆排序演算法思路很簡單 第一步,構建乙個堆 第二步,取出堆頂 第三步,如果...
工廠不上雲,等死!上雲不上鏈,找死!
歷史的路上,有三大重要革命 認知革命 農業革命 科學革命。在時間的不斷推進中,這三大革命慢慢改變人類以及其它物種。這就是時間的偉大之處 見證變化。01智慧型進化四步曲 在現代社會,網際網路到智慧型,同樣經歷幾個步驟 資訊 資料 智慧型 數字資產。資訊化是網際網路的開端。近二十幾年來,國家一直在提倡資...