看看这个常常被初级程序员弄不懂的 “事件”
2014-09-25 00:17
483 查看
众所周知在面试中,经常有些崽子面试官会问些“事件和委托”的关系,也许一路走来的程序员大多都会被问到这个,那么对于这个
高频的”事件和委托“问题,如何回击呢?首先我从最经典的一套面试题说起,用事件来实现 “猫爪老鼠“,这是一个从网上copy过来的一
个例子。
事件定义啥的什么玩意这个我就不说了,没什么意思,为了了解这个跟委托有什么关系,下面我们来看看这段代码最后生成的IL是什么样的。
1:CryEventHandler委托
这个我想大家都清楚,委托本质上是一个继承于MulticastDelegate的类,同时会生成仅有的4个方法,看下IL即知。
2:Cat类
从这个类中,我们看到了一个Cry事件,然后就是一个Cry.Invoke(),不过当你看到Invoke的时候,你是不是很怀疑Cry是不是一个委托字段呢?
其实你怀疑的是一点问题都没有,32个赞,看下IL。
从上图中我们看到了两个好玩的东西:
① field Cry 字段,完整定义如下,然来所谓的“事件字段” 其实在IL下面蜕变成了委托字段,如果你觉得很奇怪,请看第二点。
② add_Cry,remove_Cry,如果仅仅将事件字段变成委托字段,那确实是编译器在发什么神经,然来编译器还给事件配备了两个方法,这个
其实也就是事件里面+=,-=的奥秘所在,下面我们挑add_Cry方法说下,看看方法定义的IL代码是怎么样的。
很新奇,我们找到了Combine方法,这个我们都知道,原来事件中的+=,其实就是利用Combine来将当前的委托实例放到Delegate的
委托链表中(其实里面是array实现的),为了方便理解,我把上面的IL代码翻译成C#代码。
可能有些同学对IL指令不是很熟悉,没关系,我也一样,咱博客园上面有位大神飞鸟的一篇IL指令集的博文或许能帮得到你。
3:Mouse类
如果你对Cat类的IL代码琢磨的差不多的话,下面这个Mouse类就非常简单了,仅仅调用而已嘛。
这个地方最让人关心的就是: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的构造函数。
好了,说了这么多,应该也有总结性的东西出来了,原来事件是完完全全的建立在委托的基础上,你可以认为事件就是用委托来玩一个
观察者模式的,你甚至可以认为事件就是委托。没有本质区别。
高频的”事件和委托“问题,如何回击呢?首先我从最经典的一套面试题说起,用事件来实现 “猫爪老鼠“,这是一个从网上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委托
public delegate void CryEventHandler();
这个我想大家都清楚,委托本质上是一个继承于MulticastDelegate的类,同时会生成仅有的4个方法,看下IL即知。
2:Cat类
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(); } } }
从这个类中,我们看到了一个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#代码。
public class Cat { /// <summary> /// 私有的委托变量 /// </summary> private static CryEventHandler Cry; /// <summary> /// 事件生成的方法 /// </summary> /// <param name="cryEventHandler"></param> public void Add_Cry(CryEventHandler cryEventHandler) { var result = (CryEventHandler)Delegate.Combine(Cry, cryEventHandler); Interlocked.CompareExchange<CryEventHandler>(ref Cry, result, Cry); } public void Remove_Cry(CryEventHandler cryEventHandler) { var result = (CryEventHandler)Delegate.Remove(Cry, cryEventHandler); Interlocked.CompareExchange<CryEventHandler>(ref Cry, result, Cry); } public Cat() { Console.WriteLine("Cat:I'm coming."); } public virtual void OnCry() { Console.WriteLine("Cat:MiaoMiao"); if (Cry != null) { //委托专用的四个方法,invoke,begininvoke,endinvoke,ctor Cry.Invoke(); } } }
可能有些同学对IL指令不是很熟悉,没关系,我也一样,咱博客园上面有位大神飞鸟的一篇IL指令集的博文或许能帮得到你。
3:Mouse类
如果你对Cat类的IL代码琢磨的差不多的话,下面这个Mouse类就非常简单了,仅仅调用而已嘛。
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!"); } }
这个地方最让人关心的就是: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的构造函数。
public class Mouse { public Mouse() { var cryHandler = new CryEventHandler(Run); /* * 针对IL:call void Sample.Cat::add_Cry(class Sample.CryEventHandler) * 这个没有反编译好,因为我new Cat()将会再次调用构造函数。 */ new Cat().Add_Cry(cryHandler); 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!"); } }
好了,说了这么多,应该也有总结性的东西出来了,原来事件是完完全全的建立在委托的基础上,你可以认为事件就是用委托来玩一个
观察者模式的,你甚至可以认为事件就是委托。没有本质区别。
相关文章推荐
- 看看这个常常被初级程序员弄不懂的 “事件”
- 看看这个经常被0基础程序猿弄不懂的 “事件”
- 刚刚接触C 好多都不懂 大家帮忙看看 这个程序错哪了
- 不懂反函数的同学看看这个
- c#事件,委托机制 这个例子非常好哦.提供源码,.一下子就了解了,还推荐去看 皮特的故事 要学委托 事件的话,
- 使用AVAudioRecorder录音的时候模拟器可以录音但是真机不能录音的朋友来看看,这个方法可以解决你的问题哦
- 看看这个笑话,你就知道干IT的不容易了!!
- C#中窗体之间的数据传递 ----尤其是事件那个好好看看
- php还是java,不懂这个别干编程!
- quick 自定义事件,利用这个事件可以传递消息
- 大家帮我看看这个程序
- 请看看这个组合算法哪里出了错?
- 下次有人不会配unity 安卓环境,就让他看看这个吧
- 坚持不下去的时候看看这个
- [VB.NET]请大虾们帮小虾米看看这个问题
- 当你想放弃时,看看这个漫画。
- 想成为高级架构师?看看这个,或许你不再迷茫!
- KMP(不懂的仔细看看,很有意思)
- 看看这个超级有用的一种类型——匿名类型
- 卸下你的 眼镜看看这个是什么!!!好玩啊