委托与事件的IL简单分析

简单分析是指,直接看系统生成的方法,不涉及到事件的add和remove接口(今天晚上我会额外看下相关的东西),但估计应该也差不多。

起源是看了 http://www.cnblogs.com/FreeDong/archive/2012/09/27/2705372.html 这篇文章,为了验证一下(也是因为好奇)

关于委托

委托确实是类,需要创建一个对象才能使用:

源代码:

clip_image001

相关的IL代码

clip_image002

但invoke里头有个newslot,这个让我有点觉得奇怪,newslot而且是个空方法,这产生了一个疑问(后文)

clip_image003

一个引用:

如果给定的方法是new的(注意,默认就是new), 那么将返回该方法本身, (在IL中可以看到newslot)

源文档 <http://www.cnblogs.com/PurpleTide/archive/2011/09/20/2182906.html>

因此,还不明确有个地方是怎么回事

clip_image004

用ldftn拿到函数的指针,即native int,然后再newobj那个创建的类,并传入参数,取得该类的一个对象。

【TODO】然后,我们发现它callvirt,调用了Invoke方法,但是由于给定的方法是newslot的,而且里头没有实现内容,因此,这个地方还需要考虑,它到底干了什么?

与之对应的源代码片段如下:

clip_image005

==========================================================

关于event

下面看event

从一个报错信息中就可以很明显的看出来,event是一个对象。

clip_image006

出现这个报错信息的原因是,我在Main中fire了这个event,而Main是static的,因此这个event也该加static。

clip_image007

我们编译结束后,用ildasm看看IL代码。

除了我们熟知的a这delegate被编译为类之外,我们看到有几个东西和我们的event eventa有关。

一个是:

clip_image008

另一个:

clip_image009

再一个:

clip_image010

除了这个。。还有一点点别的:

clip_image011

里头是

clip_image012

这个似乎只是声明了增加和删除eventa中handler的方法。唔。。实际上,我们不能声明一个这样的函数:

clip_image013

如果这么做,那么编译时会出现错误:

clip_image014

鉴于编译器这么做了,我有点怀疑使用上头那个.event的必要性。当然,可能反射之类的要用到这个也说不定,也可能add和remove访问器需要这个。留待今天晚上探索吧。

明显的,其中的static是由于我们将eventa声明为static,而我们声明的eventb则没有这一项,但类似:

clip_image015

好吧,不纠结这个,继续看

我们看看Main的方法,里头增加了两个事件处理程序,并且fire了这个event

clip_image016

明显的,我们发现它创建了两个对象,并且调用了系统为我们生成的add_eventa。最后,当我们调用eventa的时候,和直接新建delegate的对象并fire一样,他调用了callvirt。

看看add_eventa:

clip_image017

要看懂似乎需要一些精力……上头的大致就是载入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

大概就是这样了(有点复杂,为毛要跳回去。。。)

原文地址:https://www.cnblogs.com/slayercat/p/2705786.html