加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

看看这个常常被初级程序员弄不懂的 “事件”

发布时间:2020-12-15 04:40:50 所属栏目:百科 来源:网络整理
导读:? ? ? ? 高频的”事件和委托“问题,如何回击呢?首先我从最经典的一套面试题说起,用事件来实现 “猫爪老鼠“,这是一个从网上copy过来的一 个例子。 Main( = Cat cat /span= span style="color: #0000ff;"gt;new/spanspan style="color: #000000;"gt; Cat(

? ? ? ?

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

个例子。

Main(= Cat cat </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Cat(); cat.OnCry(); Console.ReadLine(); } } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;delegate</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; CryEventHandler(); </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;class</span><span style="color: #000000;"&gt; Cat { </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;static</span> <span style="color: #0000ff;"&gt;event</span><span style="color: #000000;"&gt; CryEventHandler Cry; </span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; Cat() { Console.WriteLine(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;Cat:I'm coming.</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;); } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;virtual</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; OnCry() { Console.WriteLine(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;Cat:MiaoMiao</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;); </span><span style="color: #0000ff;"&gt;if</span> (Cry != <span style="color: #0000ff;"&gt;null</span><span style="color: #000000;"&gt;) { Cry.Invoke(); } } } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;class</span><span style="color: #000000;"&gt; Mouse { </span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; Mouse() { Cat.Cry </span>+= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; CryEventHandler(Run); Console.WriteLine(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;Mouse:I go to find something,and I must always listen cat's crying.</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;); } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; Run() { Console.WriteLine(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;Mouse:A cat is coming,I must go back!</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;); } }</span></pre>

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

1:CryEventHandler委托

CryEventHandler();

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

2:Cat类

Console.WriteLine( Console.WriteLine( (Cry != }

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

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

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

① field Cry 字段,完整定义如下,然来所谓的“事件字段” 其实在IL下面蜕变成了委托字段,如果你觉得很奇怪,请看第二点。

.field Sample.CryEventHandler Cry

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

? ? 其实

很新奇,我们找到了Combine方法,这个我们都知道,原来事件中的+=,其实就是利用Combine来将当前的委托实例放到Delegate的

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

<div class="cnblogs_code">

              
                   
           
          
                   
          
                         result = 
             Interlocked.CompareExchange(  
                         result = 
             Interlocked.CompareExchange(  
                       Console.WriteLine(  
                         Console.WriteLine( 
              (Cry !=                   
        }

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

3:Mouse类

? ? 如果你对Cat类的IL代码琢磨的差不多的话,下面这个Mouse类就非常简单了,仅仅调用而已嘛。

Cat.Cry += Console.WriteLine( Console.WriteLine( }

这个地方最让人关心的就是: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的构造函数。

cryHandler = Console.WriteLine( Console.WriteLine( }

好了,说了这么多,应该也有总结性的东西出来了,原来事件是完完全全的建立在委托的基础上,你可以认为事件就是用委托来玩一个

观察者模式的,你甚至可以认为事件就是委托。没有本质区别。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读