一篇文章徹底搞清楚c#中的委托與事件
一、什么是委托呢?
聽(tīng)著名字挺抽象,確實(shí)不好理解。面試官最喜歡考察這個(gè),而且更喜歡問(wèn):“委托和事件有何異同?”。如果對(duì)一些知識(shí)點(diǎn)沒(méi)有想明白,那么很容易被繞進(jìn)去。研究任何事物,我們不妨從它的定義開始,委托也不例外。那么先來(lái)看c#中的委托定義,先來(lái)個(gè)例子:
public delegate void GetPacage(string code);
這個(gè)委托,看起來(lái)就是個(gè)方法簽名,取包裹,需要驗(yàn)證碼。與方法簽名不同的地方,在于多了一個(gè)delegate。c#中不乏一些便利好用的語(yǔ)法,比如foreach、yield,每一個(gè)關(guān)鍵字背后都有一段故事。delegate的背后,又有什么故事呢?其實(shí)就是c#編譯器幫我們做了些什么事情。要知道這個(gè),我們得看生成的IL,如何查看IL?請(qǐng)看下圖:
vs命令行中輸入 ildasm,會(huì)打開一個(gè)反編譯的窗口,選擇我們的程序集,如下圖:
從圖中可以看出:
1、委托的本質(zhì)就是一個(gè)密封類,這個(gè)類繼承了MulticastDelegate(多播委托)
2、委托的構(gòu)造函數(shù),有兩個(gè)參數(shù),一個(gè)類型是IntPtr,用來(lái)接收方法的,如下圖:
3、可以同步調(diào)用(Invoke),也可以異步調(diào)用 (BeginInvoke、EndInvoke)
注:
1、多播委托:一個(gè)委托可以代表多個(gè)相同簽名的方法,當(dāng)委托被調(diào)用時(shí),這些方法會(huì)依次執(zhí)行
2、IntPtr表示窗口的時(shí)候,叫它“句柄”,表示方法時(shí),叫它“指針”
3、異步調(diào)用:會(huì)產(chǎn)生一個(gè)線程,異步執(zhí)行
二、委托有什么用?
在js中,并沒(méi)有提委托的概念,卻有“回調(diào)”,比如ajax回調(diào)。把一個(gè)函數(shù)傳遞到另外一個(gè)函數(shù)里執(zhí)行,是非常自然的事情。但是在c#中,不能直接把方法名傳遞進(jìn)去。所以創(chuàng)造了委托這么個(gè)類型。c#中的委托也是為了回調(diào)。委托有什么好處?舉個(gè)例子:皇帝頒發(fā)圣旨,得派一個(gè)大臣去。大臣到了目的地,宣讀圣旨后,這才得以執(zhí)行。這說(shuō)明以下兩點(diǎn):
1、委托有很好的封裝性
2、委托的實(shí)例化與它的執(zhí)行是在不同的對(duì)象中完成的
三、委托與代理
我說(shuō)的代理,是指設(shè)計(jì)模式中的代理。代理與實(shí)際對(duì)象有相同的接口,委托與實(shí)際方法有相同的方法簽名。這就是它們類似的地方。無(wú)論是相同的接口,還是相同的方法簽名,其本質(zhì)是遵循相同的協(xié)議。這是它們僅存的相似點(diǎn)。不同點(diǎn)多了,如目的不同,委托只是回調(diào),而代理是對(duì)實(shí)際對(duì)象的訪問(wèn)控制。
四、委托和事件
先看一段代碼:
public delegate void GetPacage(string code);
 public class Heater
 {
  public event EventHandler OnBoiled;
  public event GetPacage PackageHandler;
  private void RasieBoiledEvent()
  {
   if (OnBoiled == null)
   {
    Console.WriteLine("加熱完成處理訂閱事件為空");
   }
   else
   {
    OnBoiled(this, new EventArgs());
   }
  }
  public void Begin()
  {
   heatTime = 5;
   Heat();
   Console.WriteLine("加熱器已經(jīng)開啟", heatTime);
  }
  private int heatTime;
  private void Heat()
  {
   Console.WriteLine("當(dāng)前Heat Method線程:" + Thread.CurrentThread.ManagedThreadId);
   while (true)
   {
    Console.WriteLine("加熱還需{0}秒", heatTime);
    if (heatTime == 0)
    {
     RasieBoiledEvent();
     return;
    }
    heatTime--;
    Thread.Sleep(1000);
   }
  }
 }
這個(gè)是加熱器例子,為了研究事件,里面混合了自定義的委托和事件。我們看看第6行編譯后的代碼(紅框):
編譯器幫我們做了如下的事情:
1、生成了一個(gè)私有的委托字段
[CompilerGenerated, DebuggerBrowsable(DebuggerBrowsableState.Never)] private GetPacage PackageHandler;
2、生成了添加和移除委托的方法
[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);
}
這就是事件和委托的關(guān)系。有點(diǎn)像字段和屬性的關(guān)系。那有人說(shuō),事件是一種包裝的委托,或者特殊的委托,那么到底對(duì)不對(duì)呢?我覺(jué)得不對(duì)。比如我坐了公交車回家了,能說(shuō)我是一個(gè)特殊的公交車嗎?不能說(shuō)A事物擁有了B事物的能力,就說(shuō)A是特殊的B。那到底該怎么描述事件和委托之間的關(guān)系呢?事件基于委托,但并非委托??梢园咽录闯晌械拇?。在使用者看來(lái),只有事件,而沒(méi)有委托。事件是對(duì)委托的包裝,這個(gè)沒(méi)錯(cuò),到底包裝了哪些東西?
1、保護(hù)委托字段,對(duì)外不開放,所以外部對(duì)象沒(méi)法直接操作委托。提供了Add和Remove方法,供外部對(duì)象訂閱事件和取消事件
2、事件的處理方法在對(duì)象外部定義,而事件的執(zhí)行是在對(duì)象的內(nèi)部,至于事件的觸發(fā),何時(shí)何地?zé)o所謂。
五、c#鼠標(biāo)鍵盤事件
此類事件的底層實(shí)現(xiàn),一方面是消息循環(huán),另一方面是硬件中斷,或者兩者結(jié)合實(shí)現(xiàn),有空了再研究。
六、經(jīng)典面試題,貓叫、老鼠跑了,主人醒來(lái)了
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("主人醒來(lái)了");
   };
  }
 }
客戶端調(diào)用:
Cat cat = new Cat(); Mouse m = new Mouse(cat); People p = new People(cat); cat.Scream();
運(yùn)行結(jié)果:
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)我們的支持。
欄 目:C#教程
下一篇:C#使用InstallerProjects打包桌面應(yīng)用程序的完整步驟
本文標(biāo)題:一篇文章徹底搞清楚c#中的委托與事件
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/4702.html
您可能感興趣的文章
- 01-10winform 實(shí)現(xiàn)控制輸入法
 - 01-10C#編程自學(xué)之類和對(duì)象
 - 01-10C#中深度復(fù)制和淺度復(fù)制詳解
 - 01-10C#編程自學(xué)之流程控制語(yǔ)句
 - 01-10C#影院售票系統(tǒng)畢業(yè)設(shè)計(jì)(3)
 - 01-10C#影院售票系統(tǒng)畢業(yè)設(shè)計(jì)(4)
 - 01-10C#實(shí)現(xiàn)帶消息數(shù)的App圖標(biāo)
 - 01-10C#如何實(shí)現(xiàn)圖片查看器
 - 01-10談C# using的用法與好處
 - 01-10C# 后臺(tái)處理圖片的幾種方法
 


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
 - 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
 - 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
 - 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
 - 5c語(yǔ)言計(jì)算三角形面積代碼
 - 6什么是 WSH(腳本宿主)的詳細(xì)解釋
 - 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
 - 8正則表達(dá)式匹配各種特殊字符
 - 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
 - 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
 
本欄相關(guān)
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并
 - 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開的解決方案
 - 01-10WinForm限制窗體不能移到屏幕外的方法
 - 01-10WinForm繪制圓角的方法
 - 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
 - 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
 - 01-10C#停止線程的方法
 - 01-10C#實(shí)現(xiàn)清空回收站的方法
 - 01-10C#通過(guò)重寫Panel改變邊框顏色與寬度的
 - 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
 
隨機(jī)閱讀
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
 - 01-10delphi制作wav文件的方法
 - 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
 - 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
 - 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
 - 04-02jquery與jsp,用jquery
 - 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
 - 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
 - 01-10C#中split用法實(shí)例總結(jié)
 - 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
 


