<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          委托和事件 有什么區(qū)別?點(diǎn)開(kāi)看看就知道了

          共 6637字,需瀏覽 14分鐘

           ·

          2020-08-16 08:00

          一:背景

          1. 講故事

          前幾天公司一個(gè)妹子問(wèn)我,事件和委托有什么區(qū)別?先由衷感嘆一下,編碼十余年,年輕的時(shí)候常被面試官問(wèn)起,現(xiàn)在年長(zhǎng)了,卻被后輩們時(shí)常問(wèn)候,看樣子逃離編碼生涯之前是跑不掉了,不過(guò)奇怪的是,這個(gè)問(wèn)題被問(wèn)起的時(shí)候,我發(fā)現(xiàn)有很多人用:?事件是一種特殊的委托?來(lái)進(jìn)行總結(jié),是不是挺有意思,我想這句話(huà)可能來(lái)自于網(wǎng)絡(luò)上的面試題答案吧,這篇我就試著徹底總結(jié)一下。

          二:事件真的是特殊的委托嗎?

          1. 貓和老鼠 經(jīng)典案例

          要想知道兩者到底什么關(guān)系?先得有一些基礎(chǔ)代碼,這里就用大家初學(xué)事件時(shí)用到的?貓和老鼠?經(jīng)典案例,代碼簡(jiǎn)化如下:


          class Program
          {

          static void Main(string[] args)
          {
          Cat cat = new Cat("湯姆");
          Mouse mouse1 = new Mouse("杰瑞", cat);
          Mouse mouse2 = new Mouse("杰克", cat);
          cat.CatComing();
          Console.ReadKey();
          }
          }

          class Cat
          {

          public event Action CatCome; //聲明一個(gè)事件

          private string name;

          public Cat(string name)
          {
          this.name = name;
          }
          public void CatComing()
          {
          Console.WriteLine("貓" + name + "來(lái)了");
          CatCome?.Invoke();
          }
          }

          class Mouse
          {

          private string name;

          public Mouse(string name, Cat cat)
          {
          this.name = name;
          cat.CatCome += this.RunAway; //Mouse 注冊(cè) CatCome 主題
          }
          public void RunAway()
          {
          Console.WriteLine(name + "正在逃跑");
          }
          }

          代碼非常簡(jiǎn)潔,貓的 CatCome 動(dòng)作一旦觸發(fā),注冊(cè)到 CatCome 上的 兩只 mouse 就會(huì)執(zhí)行各自的逃跑動(dòng)作?RunAway,如果大家沒(méi)有看懂可以多看幾遍哈。

          2. 觀察者模式/發(fā)布訂閱模式

          如果你了解過(guò)設(shè)計(jì)模式,我想你應(yīng)該第一眼就能看出這是 觀察者模式,對(duì)的,現(xiàn)在無(wú)數(shù)的框架都在使用這個(gè)模式,比如前端的:Vue,Knockout,React,還有redis的發(fā)布訂閱等等,如果用圖畫(huà)一下大概就是這樣。

          從圖中可以看到,幾個(gè) subscribe 都訂閱了一個(gè)叫做 subject 的主題,一旦有外來(lái)的 publish 推送到了 subject,那么訂閱 subject 的 subscribe 都會(huì)收到通知,接下來(lái)根據(jù)這張圖對(duì)剛才的代碼再縷一篇:

          • 貓的?public event Action CatCome?就是一個(gè)主題 (subject)。

          • 老鼠的?cat.CatCome += this.RunAway?就是 subscribe 對(duì) subject 的訂閱。

          • 最后的?public void CatComing()?就是對(duì) subject 的推送, pubish了一條?貓來(lái)了

          3. 使用觀察者模式 對(duì) 貓鼠進(jìn)行解剖

          有了觀察者模式的基礎(chǔ),對(duì)上面的代碼進(jìn)行改造就方便多了, 我可以把?public event Action CatCome;?改成 一個(gè)?List?數(shù)組,模擬?Subject?哈,簡(jiǎn)化后的代碼如下:


          class Cat
          {

          public List Subject = new List(); //定義一個(gè)主題

          private string name;

          public Cat(string name)
          {
          this.name = name;
          }
          public void CatComing()
          {
          Console.WriteLine("貓" + name + "來(lái)了");

          Subject.ForEach(item => { item.Invoke(); });
          }
          }

          class Mouse
          {

          private string name;

          public Mouse(string name, Cat cat)
          {
          this.name = name;

          cat.Subject.Add(RunAway); //將 逃跑 方法注入到 subject 中
          }
          public void RunAway()
          {
          Console.WriteLine(name + "正在逃跑");
          }
          }

          看到這里,我想你對(duì)?事件和委托?應(yīng)該有一個(gè)大概的認(rèn)識(shí)了吧,但這里還有一個(gè)問(wèn)題,C#中的事件 真的如我寫(xiě)的觀察者模式這樣的嗎???要回答這個(gè)問(wèn)題,需要從 IL 角度看一下事件到底生成了什么。

          三:從IL角度看事件

          1. 使用 ilspy /ildasm 小工具

          首先來(lái)看一下所謂的事件到底在 IL 層面是個(gè)什么東西,如下圖:

          從圖中看其實(shí)就是兩個(gè)接收?Action?參數(shù)的?add_CatCome和?remove_CatCome方法,這兩個(gè)方法簡(jiǎn)化后的 il 代碼如下:


          .event [mscorlib]System.Action CatCome
          {
          .addon instance void ConsoleApp2.Cat::add_CatCome(class [mscorlib]System.Action)
          .removeon instance void ConsoleApp2.Cat::remove_CatCome(class [mscorlib]System.Action)
          }

          .method public hidebysig specialname
          instance void add_CatCome (
          class [mscorlib]System.Action 'value'
          )
          cil managed
          {
          // Method begins at RVA 0x2090
          // Code size 41 (0x29)
          .maxstack 3
          .locals init (
          [
          0] class [mscorlib]System.Action,
          [1] class [mscorlib]System.Action,
          [2] class [mscorlib]System.Action

          )


          IL_0000: ldarg.0
          IL_0001: ldfld
          class [mscorlib]System.Action ConsoleApp2.Cat::CatCome
          IL_0006: stloc.0
          // loop start (head: IL_0007)
          IL_000b: call
          class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
          IL_0010: castclass [mscorlib]System.Action

          IL_0017: ldflda
          class [mscorlib]System.Action ConsoleApp2.Cat::CatCome

          IL_001e: call !!
          0 [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.Action>(!!0&, !!0, !!0)

          // end loop
          IL_0028: ret
          }
          // end of method Cat::add_CatCome

          .method
          public hidebysig specialname
          instance void remove_CatCome (
          class [mscorlib]System.Action 'value'
          )
          cil managed
          {
          IL_0000: ldarg.0
          IL_0001: ldfld class [mscorlib]System.Action ConsoleApp2.Cat::CatCome
          IL_0006: stloc.0
          // loop start (head: IL_0007)
          IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
          IL_0010: castclass [mscorlib]System.Action

          IL_0017: ldflda class [mscorlib]System.Action ConsoleApp2.Cat::CatCome

          IL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.Action>(!!0&, !!0, !!0)

          IL_0026:
          bne.un.s IL_0007
          // end loop
          IL_0028: ret
          } // end of method Cat::remove_CatCome

          接下來(lái)看看?mouse?類(lèi)的注冊(cè)是怎么實(shí)現(xiàn)的。

          從圖中可以看到,所謂的注冊(cè)就是將?RunAway?作為?add_CatCome?方法的參數(shù)傳進(jìn)去而已,回過(guò)頭來(lái)看,最核心的就是那兩個(gè)所謂的?addxxx?和?removexxx?方法。

          2. 將IL代碼進(jìn)行C#還原

          可能有些同學(xué)對(duì) IL 代碼不是很熟悉,如果能還原成 C# 代碼就??了,接下來(lái)我就試著還原一下。


          class Cat
          {

          Action CatCome;

          public void add_CatCome(Action value)
          {
          Action action = this.CatCome;
          Action action2 = null;

          do
          {
          action2 = action;
          Action value2 = (Action)Delegate.Combine(action2, value);
          action = Interlocked.CompareExchange(ref this.CatCome, value2, action2);
          }
          while ((object)action != action2);
          }

          public void remove_CatCome(Action value)
          {
          Action action = this.CatCome;
          Action action2 = null;

          do
          {
          action2 = action;
          Action value2 = (Action)Delegate.Remove(action2, value);
          action = Interlocked.CompareExchange(ref this.CatCome, value2, action2);
          }
          while ((object)action != action2);
          }

          private string name;

          public Cat(string name)
          {
          this.name = name;
          }
          public void CatComing()
          {
          Console.WriteLine("貓" + name + "來(lái)了");
          CatCome?.Invoke();
          }
          }

          class Mouse
          {

          private string name;

          public Mouse(string name, Cat cat)
          {
          this.name = name;
          cat.add_CatCome(this.RunAway);
          }
          public void RunAway()
          {
          Console.WriteLine(name + "正在逃跑");
          }
          }

          可以看出還原后的C#代碼跑起來(lái)是沒(méi)有問(wèn)題的,和觀察者模式相比,這里貌似沒(méi)有看到?subject?這樣的?List?集合,但是你仔細(xì)分析的話(huà),其實(shí)是有的,你一定要著重分析這句代碼:?Action value2 = (Action)Delegate.Combine(action2, value);?它用的就是多播委托,用?Combine?方法將后續(xù)的 Action 送到前者Action的?_invocationList?中,不信的話(huà),我調(diào)試給你看哈。

          沒(méi)毛病吧,?Action CatCome?中已經(jīng)有了兩個(gè) callback 方法啦,一旦?CatCome.Invoke(), _invocationList 中的方法就會(huì)被執(zhí)行,也就看到兩只老鼠在逃跑啦。

          四:總結(jié)

          您現(xiàn)在是不是明白啦,委托和事件的關(guān)系 好比 磚頭和房子的關(guān)系,房子只是磚頭的一個(gè)應(yīng)用場(chǎng)景,您如果說(shuō)房子是一種特殊的磚,這句話(huà)品起來(lái)是不是有一種怪怪的感覺(jué),不是嗎?


          瀏覽 31
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美一级精品免费 | 大香蕉xxxx | 成年人在线观看视频 | 天天干熟女| 蜜桃精品a v久久久久久 |