這幾天比較清閑,剛好前段時間買了本CLR Via C#,在.Net界大家都知道Jeffrey大師的這本經典著作,慚愧一直沒有拜讀。
不過在C#著作中經典的非常多。讀書的那時候就是通過《C#與.NET 3.0高級程序設計》這本書接觸C#的,也被稱為C#圣經。
CLR Via C#中的委托說的非常好,比較深入,讓人耳目一新,回味無窮的感覺,看這本書的時候一定要記得帶上ILDasm和
Reflector,有了他們就可以讓我們更加深入的看一些問題,讀書的方式有很多種,提問式的,提煉式的,因為任何東西都遵循
“八二原則“,100個字中,有營養的文字也就區區20個而已,抓住了就理解了,好了,就亂扯到這里了。
Q: 什么是委托
A: 委托就是一個繼承自MulticastDelegate的類,不信可以用 ILdasm 看一下。
1 public delegate void FlyAction();
Q: 我從ILdasm中看到了Invoke方法,但是我在Delegate卻沒有看到該方法的定義?為什么?
A: 這其實是混淆了delegate關鍵字和FCL中的Delegate類型,而對于delegate關鍵字,編譯器和CLR給我們做了很多的優化,
并且掩蓋了很多復雜的細節,而FCL中的Delegate并沒有。
Q: 我知道可以將方法作為參數傳遞給委托,然后可以在任何地方通過委托的invoke來執行那個作為參數的方法,請問是如何做到的?
A:既然可以在任何地方引用這個參數的方法,那就要看看方法是如何入侵到委托的。
① 先看看實例代碼:
1 namespace Demo 2 { 3 class Program 4 { 5 public delegate void FlyAction(); 6 7 static void Main(string[] args) 8 { 9 Bird bird = new Bird(); 10 11 FlyAction action = new FlyAction(bird.Fly); 12 13 action.Invoke(); 14 } 15 } 16 17 public class Bird 18 { 19 public static Random rand = new Random(); 20 21 public void Fly() 22 { 23 return "i can fly " + rand.Next(); 24 } 25 } 26 }
從第一個QA中我們也看到了,其實委托是就是類,那我new一個類時,bird.fly其實就是類的構造函數的參數。
② 再來看看生成的IL中的構造函數。
這時候問題就出來了,為什么這里有兩個參數,而new FlyAction(bird.Fly) 中卻是一個參數,這里貌似是有問題的,不符合語法規
則,其實這里還是編譯器提供了一個中間層,通過這個中間層做了一個轉化,從而給我們隱藏了具體邏輯。其實在delegate中提供了
一個Target和Method屬性,當我們傳入bird.Fly時,Target記錄了Bird這個類,Method記錄了Bird中的Fly方法,所以invoke的時
候會自動觸發Bird類中的Fly方法。證據如下:
這里補充一點,當new FlyAction的時候如果是靜態方法,那么Target=null的,編譯器只需要通過Method就能找到觸發的方法。
Q:為什么在多播委托中,不建議回調方法有返回值,即使有返回值,多播中的方法也只能返回最后一個值,如果我想獲取每個方法
的返回值,應該怎么處理? 實例代碼如下:
1 class Program 2 { 3 public delegate string FlyAction(); 4 5 static void Main(string[] args) 6 { 7 Bird bird = new Bird(); 8 9 FlyAction action1 = new FlyAction(bird.Fly); 10 11 FlyAction action2 = new FlyAction(bird.Fly); 12 13 FlyAction action3 = new FlyAction(bird.Fly); 14 15 action1 += action2; 16 17 action1 += action3; 18 19 Console.WriteLine(action1.Invoke()); 20 21 var result = action1.GetInvocationList(); 22 23 Console.Read(); 24 } 25 } 26 27 public class Bird 28 { 29 public static Random rand = new Random(); 30 31 public string Fly() 32 { 33 return "i can fly " + rand.Next(); 34 } 35 }
A: 既然提到了“多播”,其實就是唬人了,內部源代碼里面就是維護了一個List,將“多播”中的方法都放入到List中,Invoke的時候,
就循環遍歷下List來依次調用里面的方法,這就是為什么建議不要用“有返回值“的方法。
下面我們可以通過 GetInvocationList 來獲取這個list里面的方法。
然后我們再來看看這個GetInvocationList 里面的代碼是怎么寫的。
看到了this._invocationList和for循環,是不是有一種徹底明白的感覺,如果你想獲取每個方法的返回值,那只能通過
GetInvocationList拿出來后,自己手工處理了,只有這樣才能拿到“多播委托”中每個方法的返回值。
Q:請問下最后一個問題,問完就睡覺,請問委托可以動態創建嗎?
A:可以的。Delegate中提供了CreateDelegate方法,就是可以動態創建的,舉個例子你就知道了。
1 class Program 2 { 3 public delegate string FlyAction(); 4 5 static void Main(string[] args) 6 { 7 Bird bird = new Bird(); 8 9 //找到類下的方法 10 var method = typeof(Bird).GetMethod("Fly", BindingFlags.Instance | BindingFlags.Public); 11 12 var mydelegate = (FlyAction)Delegate.CreateDelegate(typeof(FlyAction), bird, method); 13 14 var result = mydelegate.Invoke(); 15 } 16 } 17 18 public class Bird 19 { 20 public string Fly() 21 { 22 return "i can fly " + new Random().Next(); 23 } 24 }
文章列表
留言列表