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

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

概述       众所周知在面试中,经常有些崽子面试官会问些“事件和委托”的关系,也许一路走来的程序员大多都会被问到这个,那么对于这个高频的”事件和委托“问题,如何回击呢?首先我从最经典的一套面试题说起,用事件来实现 “猫爪老鼠“,这是一个从网上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 Cat2 {3 public static event CryEventHandler Cry;45 public Cat()6 {7 Console.WriteLine("Cat:I'm coming.");8 }910 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 Cat2 {3 /// <summary>4 /// 私有的委托变量5 /// </summary>6 private static CryEventHandler Cry;78 /// <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);1516 Interlocked.CompareExchange<CryEventHandler>(ref Cry, result, Cry);17 }1819 public void Remove_Cry(CryEventHandler cryEventHandler)20 {21 var result = (CryEventHandler)Delegate.Remove(Cry, cryEventHandler);2223 Interlocked.CompareExchange<CryEventHandler>(ref Cry, result, Cry);24 }2526 public Cat()27 {28 Console.WriteLine("Cat:I'm coming.");29 }3031 public virtual void OnCry()32 {33 Console.WriteLine("Cat:MiaoMiao");3435 if (Cry != null)36 {37 //委托专用的四个方法,invoke,begininvoke,endinvoke,ctor38 Cry.Invoke();39 }40 }41 }可能有些同学对IL指令不是很熟悉,没关系,我也一样,咱博客园上面有位大神飞鸟的一篇IL指令集的博文或许能帮得到你。 3:Mouse类    如果你对Cat类的IL代码琢磨的差不多的话,下面这个Mouse类就非常简单了,仅仅调用而已嘛。1 public class Mouse2 {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 }89 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 Mouse2 {3 public Mouse()4 {5 var cryHandler = new CryEventHandler(Run);67 /*8 * 针对IL:call void Sample.Cat::add_Cry(class Sample.CryEventHandler)9 * 这个没有反编译好,因为我new Cat()将会再次调用构造函数。10 */11 new Cat().Add_Cry(cryHandler);1213 Console.WriteLine("Mouse:I go to find something,and I must always listen cat's crying.");14 }1516 public void Run()17 {18 Console.WriteLine("Mouse:A cat is coming,I must go back!");19 }20 } 好了,说了这么多,应该也有总结性的东西出来了,原来事件是完完全全的建立在委托的基础上,你可以认为事件就是用委托来玩一个观察者模式的,你甚至可以认为事件就是委托。没有本质区别。

       

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

个例子。

Main(= Cat cat </span>= <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Cat(); cat.OnCry(); Cons<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a>e.Read<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(); }}</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;delegate</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;vo<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a></span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; CryEventHandler();</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;class</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Cat{ </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;static</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;event</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; CryEventHandler Cry; </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Cat() { Cons<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a>e.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;Cat:I'm coming.</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;); } </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;virtual</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;vo<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a></span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; OnCry() { Cons<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a>e.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;Cat:MiaoMiao</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;); </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;if</span> (Cry != <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;n<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>l</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;) { Cry.Invoke(); } }}</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;class</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Mouse{ </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Mouse() { Cat.Cry </span>+= <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;new</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; CryEventHandler(Run); Cons<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a>e.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;Mouse:I go to find something,and I must always <a href="https://m.jb51.cc/tag/List/" target="_blank" >List</a>en cat's crying.</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt;); } </span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c</span> <span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #0000ff;"&gt;vo<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a></span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #000000;"&gt; Run() { Cons<a href="https://m.jb51.cc/tag/ol/" target="_blank" >ol</a>e.Write<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>ne(</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;Mouse:A cat is coming,I must go back!</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #800000;"&gt;"</span><span https://m.jb51.cc/tag/color/" target="_blank" >color</a>: #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 >

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

可能有些同学对IL指令不是很熟悉,没关系,我也一样,咱博客园上面有位大神飞鸟的一篇href="http://t.cn/zQfip90" target="_blank">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( }

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

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

总结

以上是内存溢出为你收集整理的看看这个常常被初级程序员弄不懂的 “事件”全部内容,希望文章能够帮你解决看看这个常常被初级程序员弄不懂的 “事件”所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1264084.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存