• 委托与事件的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

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

  • 相关阅读:
    论文笔记4
    论文笔记3
    论文笔记2
    论文笔记1
    论文笔记
    AFG与AWG的比较
    Linux下“有线线缆被拔出”问题的解决
    python生成excel格式座位表
    PythonTip--一马当先--bfs
    python pygame--倒计时
  • 原文地址:https://www.cnblogs.com/slayercat/p/2705786.html
Copyright © 2020-2023  润新知