1、什麼是委託呢?面試
聽着名字挺抽象,確實很差理解。面試官最喜歡考察這個,並且更喜歡問:「委託和事件有何異同?」。若是對一些知識點沒有想明白,那麼很容易被繞進去。研究任何事物,咱們不妨從它的定義開始,委託也不例外。那麼先來看c#中的委託定義,先來個例子:ajax
public delegate void GetPacage(string code);
這個委託,看起來就是個方法簽名,取包裹,須要驗證碼。與方法簽名不一樣的地方,在於多了一個delegate。c#中不乏一些便利好用的語法,好比foreach、yield,每個關鍵字背後都有一段故事。delegate的背後,又有什麼故事呢?其實就是c#編譯器幫咱們作了些什麼事情。要知道這個,咱們得看生成的IL,如何查看IL?請看下圖:c#
vs命令行中輸入 ildasm,會打開一個反編譯的窗口,選擇咱們的程序集,以下圖:設計模式
從圖中能夠看出:異步
一、委託的本質就是一個密封類,這個類繼承了MulticastDelegate(多播委託)函數
二、委託的構造函數,有兩個參數,一個類型是IntPtr,用來接收方法的,以下圖:this
三、能夠同步調用(Invoke),也能夠異步調用 (BeginInvoke、EndInvoke)spa
注:命令行
一、多播委託:一個委託能夠表明多個相同簽名的方法,當委託被調用時,這些方法會依次執行線程
二、IntPtr表示窗口的時候,叫它「句柄」,表示方法時,叫它「指針」
三、異步調用:會產生一個線程,異步執行
2、委託有什麼用?
在js中,並無提委託的概念,卻有「回調」,好比ajax回調。把一個函數傳遞到另一個函數裏執行,是很是天然的事情。可是在c#中,不能直接把方法名傳遞進去。因此創造了委託這麼個類型。c#中的委託也是爲了回調。委託有什麼好處?舉個例子:皇帝頒發聖旨,得派一個大臣去。大臣到了目的地,宣讀聖旨後,這才得以執行。這說明如下兩點:
一、委託有很好的封裝性
二、委託的實例化與它的執行是在不一樣的對象中完成的
3、委託與代理
我說的代理,是指設計模式中的代理。代理與實際對象有相同的接口,委託與實際方法有相同的方法簽名。這就是它們相似的地方。不管是相同的接口,仍是相同的方法簽名,其本質是遵循相同的協議。這是它們僅存的類似點。不一樣點多了,如目的不一樣,委託只是回調,而代理是對實際對象的訪問控制。
4、委託和事件
先看一段代碼:
1 public delegate void GetPacage(string code); 2 public class Heater 3 { 4 public event EventHandler OnBoiled; 5 6 public event GetPacage PackageHandler; 7 8 private void RasieBoiledEvent() 9 { 10 11 if (OnBoiled == null) 12 { 13 Console.WriteLine("加熱完成處理訂閱事件爲空"); 14 } 15 else 16 { 17 OnBoiled(this, new EventArgs()); 18 } 19 } 20 public void Begin() 21 { 22 heatTime = 5; 23 Heat(); 24 Console.WriteLine("加熱器已經開啓", heatTime); 25 26 } 27 private int heatTime; 28 private void Heat() 29 { 30 Console.WriteLine("當前Heat Method線程:" + Thread.CurrentThread.ManagedThreadId); 31 while (true) 32 { 33 Console.WriteLine("加熱還需{0}秒", heatTime); 34 35 if (heatTime == 0) 36 { 37 RasieBoiledEvent(); 38 return; 39 } 40 heatTime--; 41 Thread.Sleep(1000); 42 43 } 44 } 45 }
這個是加熱器例子,爲了研究事件,裏面混合了自定義的委託和事件。咱們看看第6行編譯後的代碼(紅框):
編譯器幫咱們作了以下的事情:
一、生成了一個私有的委託字段
[CompilerGenerated, DebuggerBrowsable(DebuggerBrowsableState.Never)] private GetPacage PackageHandler;
二、生成了添加和移除委託的方法
[CompilerGenerated] public void add_PackageHandler(GetPacage value) { GetPacage pacage2; GetPacage packageHandler = this.PackageHandler; do { pacage2 = packageHandler; GetPacage pacage3 = (GetPacage) Delegate.Combine(pacage2, value); packageHandler = Interlocked.CompareExchange<GetPacage>(ref this.PackageHandler, pacage3, pacage2); } while (packageHandler != pacage2); }
這就是事件和委託的關係。有點像字段和屬性的關係。那有人說,事件是一種包裝的委託,或者特殊的委託,那麼到底對不對呢?我以爲不對。好比我坐了公交車回家了,能說我是一個特殊的公交車嗎?不能說A事物擁有了B事物的能力,就說A是特殊的B。那到底該怎麼描述事件和委託之間的關係呢?事件基於委託,但並不是委託。能夠把事件當作委託的代理。在使用者看來,只有事件,而沒有委託。事件是對委託的包裝,這個沒錯,到底包裝了哪些東西?
一、保護委託字段,對外不開放,因此外部對象無法直接操做委託。提供了Add和Remove方法,供外部對象訂閱事件和取消事件
二、事件的處理方法在對象外部定義,而事件的執行是在對象的內部,至於事件的觸發,什麼時候何地無所謂。
5、c#鼠標鍵盤事件
此類事件的底層實現,一方面是消息循環,另外一方面是硬件中斷,或者二者結合實現,有空了再研究。
6、經典面試題,貓叫、老鼠跑了,主人醒來了
public delegate void ScreamHandler(); public class Cat { public event ScreamHandler OnScream; public void Scream() { Console.WriteLine("貓叫了一聲"); OnScream?.Invoke(); } } public class Mouse { public Mouse(Cat c) { c.OnScream += () => { Console.WriteLine("老鼠跑了"); }; } } public class People { public People(Cat c) { c.OnScream += () => { Console.WriteLine("主人醒來了"); }; } }
客戶端調用:
Cat cat = new Cat(); Mouse m = new Mouse(cat); People p = new People(cat); cat.Scream();
運行結果: