技术开发 频道

.NET面面观——探索.Net中的委托

  接着看看AssignDelegate反编译后的代码:

.method public hidebysig instance void  AssignDelegate() cil managed  
{  
  
// Code size       19 (0x13)  
   .maxstack  8  
//将方法的第一个参数push到IL的运算栈上(对于一个实例方法来说,比如AssignDelegate,它的第一个参数就是“this”了)  
   IL_0000:  ldarg.0  
//这里又把this压栈了一次,因为下面一条指令中的Test方法是一个实例方法,需要一个this  
   IL_0001:  ldarg.0  
//ldftn就是把实现它的参数中的方法的本机代码的非托管指针push到栈上,在这里你就可以认为是获取实例方法Test的地址  
   IL_0002:  ldftn instance void Yuyijq.DotNet.Chapter2.TestDelegate::Test(int32)  
//调用委托的构造器,这个构造器需要两个参数,一个对象引用,就是第一次压栈的this,一个方法的地址。  
   IL_0008:  newobj instance void Yuyijq.DotNet.Chapter2.MyDelegate::.ctor(object,native int)  
   IL_000d:  stfld
class Yuyijq.DotNet.Chapter2.MyDelegate Yuyijq.DotNet.Chapter2.TestDelegate::myDelegate  
   IL_0012:  ret  
}

 

  通过上面的代码,我们会发现,将一个实例方法分配给委托时,委托不仅仅引用了方法的地址,还有这个方法所在对象的引用,这里就是所谓的类型安全。

  我们再回过头来看看MyDelegate的继承链:MyDelegate->MulticastDelegate->Delegate。

  奇妙的地方

  而Delegate中有三个有趣的字段:

  internal object _target;

  internal IntPtr _methodPtr;

  internal IntPtr _methodPtrAux;

  对这三个字段做详细说明

  _target

  1、如果委托指向的方法是实例方法,则_target的值是指向目标方法所在对象的指针

  2、如果委托指向的是静态方法,则_target的值是委托实例自身

  _methodPtr

  1、如果委托指向的方法是实例方法,则_methodPtr的值指向一个JIT Stub(如果这个方法还没有被JIT编译,关于JIT Stub会在后面的章节介绍),或指向该方法JIT后的地址

  2、如果委托指向的方法是静态方法,则_methodPtr指向的是一个Stub(一段小代码,这段代码的作用是_target,然后调用_methodPtrAux指向的方法),而且所有签名相同的委托,共享这个Stub。为什么要这样一个Stub?我想是为了让通过委托调用方法的流程一致吧,不管指向的是实例方法还是静态方法,对于外部来说,只需要调用_methodPtr指向的地址,但是对于调用实例方法而言,它需要this,也就是这里的_target,而静态方法不需要,为了让这里的过程一直,CLR会偷偷的在委托指向静态方法时插入一小段代码,用于去掉_target,而直接jmp到_methodPtrAux指向的方法。

  _methodPtrAux

  1、如果委托指向的是实例方法,则_methodPtrAux就是0。

  2、如果委托指向的是静态方法,则这时_methodPtrAux起的作用与_mthodPtr在委托指向实例方法的时候是一样的。

  实际上通过反编译Delegate的代码发现,Delegate有一个只读属性Target,该Target的实现依靠GetTarget方法,该方法的代码如下:

internal virtual object GetTarget()  
{  
    
if (!this._methodPtrAux.IsNull())  
     {  
        
return null;  
     }  
    
return this._target;  
}

  实了当委托指向静态方法时,Target属性为null。

0
相关文章