我們使用動態代理,主要是因為動態代理擁有這樣的能力--使得某個類型A在啟動並執行時候能轉化為一個指定的介面I,即使這個類型A在定義的時候並沒有從這個指定的介面I繼承。這句話是什麼意思了?還是回到當泛型的參數類型是動態... 一文中的那個例子,在例子中,List<>並沒有從ISimpleList繼承,但是從表面看來,在啟動並執行時候,我們通過動態代理可以以ISimpleList介面來“引用”List<>類型的對象。
動態代理是在運行時在記憶體中構建的一種類型,該類型實現了介面I,但是它將所有的方法調用都轉寄給類型A。
注意,上面我使用了“方法”調用的轉寄,由於,事件、屬性都是方法的變體,所以,對介面中定義的所有元素的Call都可以被動態代理轉寄。那麼,介面(I)中的方法與被代理者(Target)的方法如何匹配起來了?通常的方法是,進行“同名”匹配,比如ISimpleList介面的Add方法就自然匹配到List<>的Add方法。對於複雜的需求,可以定義一個方法名映射表來匹配不同名的方法。
由於,動態代理擁有這種為類型(Target)“換臉”的能力,所以,在很多場合可以使用它來優雅地解決一些以前難以處理的問題(通常,以前我們使用反射來解決這些麻煩),比如:
(1)“泛型參數類型是動態”,使用動態代理解決這種問題不僅可以避免反射帶來的效能損失,而且還可以獲得強型別方法調用的好處。
(2)為一群組類型“變臉”。比如,TextBox、RichTextBox、ListView等windows控制項都有Clear方法,但是它們都沒有實現一個統一的介面(比如,該介面中定義了Clear方法),所以當我要清空某個GroupBox中所有控制項的內容時,無法用一種統一的方式調用,你不能這樣做:
以下為引用的內容: foreach (Control control in this.groupBox1.Controls) { control.Clear(); //Control不存在Clear方法,編譯報錯 } |
但是有了動態代理之後,我們就可以為這些控制項定義一個New Face:
以下為引用的內容:
public interface INewFace { void Clear(); } |
然後優雅地這樣調用:
以下為引用的內容:
foreach (Control control in this.groupBox1.Controls) { INewFace face = DynamicTypeEmitter.CreateDynamicProxy<INewFace>(control); face.Clear(); } |
你可以繼續挖掘使用動態代理的其它場合,發揮你的想象力,來展現動態代理的威力。
關於ESBasic中的 DynamicTypeEmitter 實現,有一點缺憾,那就是暫時還不支援“泛型方法”的調用轉寄,我還不知道如何使用Emit發射對動態型別參數的泛型方法的調用,有知道的朋友請指導下。