简单分析是指,直接看系统生成的方法,不涉及到事件的add和remove接口(今天晚上我会额外看下相关的东西),但估计应该也差不多。
起源是看了 http://www.cnblogs.com/FreeDong/archive/2012/09/27/2705372.html 这篇文章,为了验证一下(也是因为好奇)
委托确实是类,需要创建一个对象才能使用:
源代码:
相关的IL代码
但invoke里头有个newslot,这个让我有点觉得奇怪,newslot而且是个空方法,这产生了一个疑问(后文)
一个引用:
如果给定的方法是new的(注意,默认就是new), 那么将返回该方法本身, (在IL中可以看到newslot)
源文档 <http://www.cnblogs.com/PurpleTide/archive/2011/09/20/2182906.html>
因此,还不明确有个地方是怎么回事
用ldftn拿到函数的指针,即native int,然后再newobj那个创建的类,并传入参数,取得该类的一个对象。
【TODO】然后,我们发现它callvirt,调用了Invoke方法,但是由于给定的方法是newslot的,而且里头没有实现内容,因此,这个地方还需要考虑,它到底干了什么?
与之对应的源代码片段如下:
==========================================================
下面看event
从一个报错信息中就可以很明显的看出来,event是一个对象。
出现这个报错信息的原因是,我在Main中fire了这个event,而Main是static的,因此这个event也该加static。
我们编译结束后,用ildasm看看IL代码。
除了我们熟知的a这delegate被编译为类之外,我们看到有几个东西和我们的event eventa有关。
一个是:
另一个:
再一个:
除了这个。。还有一点点别的:
里头是
这个似乎只是声明了增加和删除eventa中handler的方法。唔。。实际上,我们不能声明一个这样的函数:
如果这么做,那么编译时会出现错误:
鉴于编译器这么做了,我有点怀疑使用上头那个.event的必要性。当然,可能反射之类的要用到这个也说不定,也可能add和remove访问器需要这个。留待今天晚上探索吧。
明显的,其中的static是由于我们将eventa声明为static,而我们声明的eventb则没有这一项,但类似:
好吧,不纠结这个,继续看
我们看看Main的方法,里头增加了两个事件处理程序,并且fire了这个event
明显的,我们发现它创建了两个对象,并且调用了系统为我们生成的add_eventa。最后,当我们调用eventa的时候,和直接新建delegate的对象并fire一样,他调用了callvirt。
看看add_eventa:
要看懂似乎需要一些精力……上头的大致就是载入eventa并且将传入的delegate a的对象用combine加起来,下头的部分东西还没有接触过,例如!!0,根据
http://stackoverflow.com/questions/12234345/how-to-emit-event-at-runtime
的说法,是泛型调用的泛型参数。0表示是第0个泛型参数。
好吧,那么看上头的堆栈,局部变量0和局部变量1都是eventa,局部变量2存储了combine的结果,然后载入了eventa的地址到堆栈(ldsflda),然后载入2(combine的结果),再载入1(combine之前的结果)。
调用Interlocked.CompareExchange<T> 方法:
http://msdn.microsoft.com/zh-cn/library/bb297966.aspx
大概意思是参数0引用的对象若和参数2这个对象相等,则将参数1赋值给参数0。这个过程是原子化操作的
那么,若引用的对象是同一个,那么久将combine的结果赋值给eventa,大致是这个意思。返回的原始值放到了局部变量0里头。再载入局部变量1,进行ceq判断是不是相等。。不相等就是0,相等就是1
再判断结果是不是0,将结果存在3号局部变量中。额。。不相等3号局部变量就是true,相等是false……如果3号为真(不相等),那么跳转到6号指令(好麻烦)。。。
remove与之类似,只是调用的函数换成了[mscorlib]System.Delegate::Remove
大概就是这样了(有点复杂,为毛要跳回去。。。)