public delegate void DemoDelegate(string strName, int iAge);
static void Main(string[] args)
{
DemoDelegate fbStatic = new DemoDelegate(Program.StaticPrintInfo);
fbStatic("jie_Peng", 23);
DemoDelegate fbInstance = new DemoDelegate(new Program().PrintInfo);
fbInstance.Invoke("jie_Peng", 24);
}
public delegate void DemoDelegate(string strName, int iAge);
private static void StaticPrintInfo(string myName, int myAge)
{
Console.WriteLine(string.Format("I am from Static Method. My name is {0}, and I am {1} years old !", myName, myAge));
}
private void PrintInfo(string strName, int iAge)
{
Console.WriteLine(string.Format("I am from object method ! My name is {0}, and I am {1} years old !", strName, iAge));
}
输出如下:
观察图中的代码,我们可以发现,委托的实例对象,就相当于是方法的一个包装器,使方法能够通过包装器来间接的回调。我们发现,回调函数的调用,我们使用了两种不同的方法,他们真的不同吗?实际上,他们是相同的。编译器会将 fbStatic("jie_Peng",23) 编译为 fbStatic.Invoke("jie_Peng", 23) 。
接下来,我们来讨论一下编译器对定义的委托 DemoDelegate 都做了些什么处理。
我们发现,编译器为我们生成了一个类 DemoDelegate ,该类继承自 System.MulticastDelegate (所有的委托类型都派生自该类),而后者又继承自 System.Delegate(继承自 System.Object) 类。编译器为我们生成了4个方法:一个构造器、Invoke、BeginInvoke、EndInvoke。
我们先来分析构造函数: public DemoDelegate(Object obj, IntPtr method) 。你可能就疑惑了,我们的初始化委托对象的语句分明就是 DemoDelegate fbStatic = new DemoDelegate(Program.StaticPrintInfo); 怎么和类 DemoDelegate 构造函数不匹配呢?这如何又能通过编译呢?在回答这个问题之前,我们先来介绍一下所有委托的父类 System.MulticastDelegate 中的三个私有成员:object _target , IntPtr _methodPtr , object _invocationList 。 接下来我们对这3个私有成员一一做出解释。
_target :当委托对象包装一个静态方法时,这个字段为 null 。当委托对象包装一个实例方法时,这个字段引用的是回调方法要操作的对象。也就是说,这个字段指出要传给实例方法的隐式参数this的值(该对象指向调用回调函数的实例对象)。
_methodPtr :一个内部整数值,CLR用它标识要回调的方法。
_invocationList :该字段通常为null。构造委托链时,它可以引用一个委托数组。
好了,我们现在来分析类 DemoDelegate 的构造函数。该构造函数有两个参数,object 和 IntPtr ,你们是不是已经猜到这2个参数的意思了?对的,当编译器知道要初始化的对象是委托对象时,所以会分析源代码,来确定引用的是哪个对象和哪个方法。然后在构造函数内将对象的值赋值 obj 赋值给 _target , 将 method 赋值给 _methodPtr ,并将 _invovationList 也置为 null ,对于传入的静态方法,则将 _target 置为 null 。如下图,我们来看看 fbStatic 和 fbInstance 对应的私有字段的赋值情况。
讲到这里,你可能在想,那我们该如何获取到这些私有字段呢?这个 FCL 已经为我们提供了访问的方式,即 System.Delegate 的两个属性:Target 和 Method 。Target 返回的是 _target 字段的值,而 Method 返回的是封装了 _methodPtr 后的 System.Reflection.MethodInfo 对象。