文章出處

 

  這幾天比較清閑,剛好前段時間買了本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     }

 

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()