1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 6637字,需瀏覽 14分鐘

         ·

        2020-08-16 08:00

        一:背景

        1. 講故事

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

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

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

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


        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 + "來了");
        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 + "正在逃跑");
        }
        }

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

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

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

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

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

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

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

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

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


        class Cat
        {

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

        private string name;

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

        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è)問題,C#中的事件 真的如我寫的觀察者模式這樣的嗎???要回答這個(gè)問題,需要從 IL 角度看一下事件到底生成了什么。

        三:從IL角度看事件

        1. 使用 ilspy /ildasm 小工具

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

        從圖中看其實(shí)就是兩個(gè)接收?Action?參數(shù)的?add_CatCome和?remove_CatCome方法,這兩個(gè)方法簡化后的 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

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

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

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

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


        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 + "來了");
        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#代碼跑起來是沒有問題的,和觀察者模式相比,這里貌似沒有看到?subject?這樣的?List?集合,但是你仔細(xì)分析的話,其實(shí)是有的,你一定要著重分析這句代碼:?Action value2 = (Action)Delegate.Combine(action2, value);?它用的就是多播委托,用?Combine?方法將后續(xù)的 Action 送到前者Action的?_invocationList?中,不信的話,我調(diào)試給你看哈。

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

        四:總結(jié)

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


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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产久久精品做爱片 | 大陆黄色视频 | 久久精品—区二区三区舞蹈 | 日本丝袜脚交视频 | 老女人做爰全过程免费看 | 国产香蕉97碰碰碰视频 | 丰满秘书高清大乳在线观看0 | 男女爆操视频 | 李采潭三点尽露三级 | 国产男女视频在线观看 |