zoukankan      html  css  js  c++  java
  • 看看这个经常被0基础程序猿弄不懂的 “事件”

    众所周知在面试中。常常有些崽子面试官会问些“事件和托付”的关系,或许一路走来的程序猿大多都会被问到这个。那么对于这个

    高频的”事件和托付“问题,怎样回击呢?首先我从最经典的一套面试题说起,用事件来实现 “猫爪老鼠“,这是一个从网上copy过来的一

    个样例。

    复制代码
            static void Main(string[] args)
            {
                Mouse mouse = new Mouse();
    
                Cat cat = new Cat();
    
                cat.OnCry();
    
                Console.ReadLine();
            }
        }
    
        public delegate void CryEventHandler();
    
        public class Cat
        {
            public static event CryEventHandler Cry;
    
            public Cat()
            {
                Console.WriteLine("Cat:I'm coming.");
            }
    
            public virtual void OnCry()
            {
                Console.WriteLine("Cat:MiaoMiao");
                if (Cry != null)
                {
                    Cry.Invoke();
                }
            }
        }
    
        public class Mouse
        {
            public Mouse()
            {
                Cat.Cry += new CryEventHandler(Run);
                Console.WriteLine("Mouse:I go to find something,and I must always listen cat's crying.");
            }
    
            public void Run()
            {
                Console.WriteLine("Mouse:A cat is coming,I must go back!");
            }
        }
    复制代码

     事件定义啥的什么玩意这个我就不说了。没什么意思。为了了解这个跟托付有什么关系,以下我们来看看这段代码最后生成的IL是什么样的。

     

    1:CryEventHandler托付

    1 public delegate void CryEventHandler();

        这个我想大家都清楚,托付本质上是一个继承于MulticastDelegate的类,同一时候会生成仅有的4个方法,看下IL即知。

     

    2:Cat类

    复制代码
     1 public class Cat
     2     {
     3         public static event CryEventHandler Cry;
     4 
     5         public Cat()
     6         {
     7             Console.WriteLine("Cat:I'm coming.");
     8         }
     9 
    10         public virtual void OnCry()
    11         {
    12             Console.WriteLine("Cat:MiaoMiao");
    13             if (Cry != null)
    14             {
    15                 Cry.Invoke();
    16             }
    17         }
    18     }
    复制代码

    从这个类中,我们看到了一个Cry事件,然后就是一个Cry.Invoke()。只是当你看到Invoke的时候,你是不是非常怀疑Cry是不是一个托付字段呢?

    事实上你怀疑的是一点问题都没有,32个赞,看下IL。

     

    从上图中我们看到了两个好玩的东西:

    ① field Cry 字段,完整定义例如以下,然来所谓的“事件字段” 事实上在IL以下蜕变成了托付字段,假设你认为非常奇怪,请看第二点。

    .field private static class Sample.CryEventHandler Cry

    ② add_Cry,remove_Cry,假设只将事件字段变成托付字段,那确实是编译器在发什么神经。然来编译器还给事件配备了两个方法,这个

        事实上也就是事件里面+=。-=的奥秘所在,以下我们挑add_Cry方法说下,看看方法定义的IL代码是怎么样的。

     

    非常新鲜,我们找到了Combine方法。这个我们都知道。原来事件中的+=,事实上就是利用Combine来将当前的托付实例放到Delegate的

    托付链表中(事实上里面是array实现的),为了方便理解,我把上面的IL代码翻译成C#代码。

     

    复制代码
     1  public class Cat
     2     {
     3         /// <summary>
     4         /// 私有的托付变量
     5         /// </summary>
     6         private static CryEventHandler Cry;
     7 
     8         /// <summary>
     9         /// 事件生成的方法
    10         /// </summary>
    11         /// <param name="cryEventHandler"></param>
    12         public void Add_Cry(CryEventHandler cryEventHandler)
    13         {
    14             var result = (CryEventHandler)Delegate.Combine(Cry, cryEventHandler);
    15 
    16             Interlocked.CompareExchange<CryEventHandler>(ref Cry, result, Cry);
    17         }
    18 
    19         public void Remove_Cry(CryEventHandler cryEventHandler)
    20         {
    21             var result = (CryEventHandler)Delegate.Remove(Cry, cryEventHandler);
    22 
    23             Interlocked.CompareExchange<CryEventHandler>(ref Cry, result, Cry);
    24         }
    25 
    26         public Cat()
    27         {
    28             Console.WriteLine("Cat:I'm coming.");
    29         }
    30 
    31         public virtual void OnCry()
    32         {
    33             Console.WriteLine("Cat:MiaoMiao");
    34 
    35             if (Cry != null)
    36             {
    37                 //托付专用的四个方法。invoke,begininvoke,endinvoke,ctor
    38                 Cry.Invoke();
    39             }
    40         }
    41     }
    复制代码

    可能有些同学对IL指令不是非常熟悉。没关系,我也一样,咱博客园上面有位大神飞鸟的一篇IL指令集的博文也许能帮得到你。

     

    3:Mouse类

        假设你对Cat类的IL代码琢磨的几乎相同的话。以下这个Mouse类就很easy了,只调用而已嘛。

    复制代码
     1     public class Mouse
     2     {
     3         public Mouse()
     4         {
     5             Cat.Cry += new CryEventHandler(Run);
     6             Console.WriteLine("Mouse:I go to find something,and I must always listen cat's crying.");
     7         }
     8 
     9         public void Run()
    10         {
    11             Console.WriteLine("Mouse:A cat is coming,I must go back!");
    12         }
    13     }
    复制代码

    这个地方最让人关心的就是:Cat.Cry += new CryEventHandler(Run) 这个语句,从它的IL中能够看到事实上做了三件事。

    ① ldftn:       将Run方法的非托管指针推送到计算堆栈上。

    ② newobj:   将CryEventHandler托付new一下,同一时候将计算堆栈上的Run方法的非托管指针作为构造函数的參数。

    ③ call:         调用Cat类的Add_Cry方法。将CryEventHandler的实例作为參数传递下去。

     

    以下继续将该IL代码反编译回来,只是针对IL指令:call       void Sample.Cat::add_Cry(class Sample.CryEventHandler)

    并没有非常好的翻译过来,仅仅能new Cat()了一下才干调用Add_Cry,从而触发了Cat的构造函数。

    复制代码
     1   public class Mouse
     2     {
     3         public Mouse()
     4         {
     5             var cryHandler = new CryEventHandler(Run);
     6 
     7             /*
     8              *  针对IL:call       void Sample.Cat::add_Cry(class Sample.CryEventHandler)
     9              *  这个没有反编译好,由于我new Cat()将会再次调用构造函数。
    10              */
    11             new Cat().Add_Cry(cryHandler);
    12 
    13             Console.WriteLine("Mouse:I go to find something,and I must always listen cat's crying.");
    14         }
    15 
    16         public void Run()
    17         {
    18             Console.WriteLine("Mouse:A cat is coming,I must go back!");
    19         }
    20     }
    复制代码

     

    好了,说了这么多。应该也有总结性的东西出来了,原来事件是完全然全的建立在托付的基础上。你能够觉得事件就是用托付来玩一个

    观察者模式的,你甚至能够觉得事件就是托付。没有本质差别。

  • 相关阅读:
    Samba
    百度贴吧
    baidu.com关键字查询
    vim 删除每行开头结尾空格
    read line(逐行读取)
    pycharm 激活
    rsync 启动脚本
    收藏
    elk
    sql is null
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7127986.html
Copyright © 2011-2022 走看看