面試中可能會被問到的幾個關於“委託”的問題

一線碼農發表於2014-07-03

 

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

 

 

相關文章