间接方法调用和委托
前面的讨论主要着眼于Methodinfo对象,开发人员可以藉此在类型兼容的对象上调用特定方法.因为MethodInfo对象属于一个类型(type)而不是一对象,因此,在使用MethodInfo调用方法时,需要在每次调用方法时都显式地提供目标对象的引用。在很多方面,这都能满足要求。不过,你还经常需要将一个特定的方法绑定到一个特定的对象上,这就需要用到委托(delegate)。
委托提供了一种机制,用于绑定一个特定方法到特定目标对象上。对于特定目标对象的绑定,委托不同于MethodInfo.Invoke,它不用在调用时显式地提供目标对象引用。因此,委托除了调用其底层方法外,几乎没做什么事情。
委托被用于基于CLR的库(library)中,表示调用特定方法的能力。因此,委托与一个单个方法
接口很相似,其主要区别在于:接口需要目标方法的类型预先声明与该接口兼容。相比之下,委托可以被绑定到任何类型的方法,只要提供的方法签名与委托类型所期望的方法签名相匹配。
如图6.6所示,委托是一个对象,它维护两个字段:一个是方法指针,一个是目标对象引用。其中,方法指针是一个C++风格的函数指针,例如,由system.RuntimeMothodHandle.GetFunctionPoiner所返回的地址:而目标对象的引用则是一个System.Object类型的引用。当委托被绑定到一个静态方法时,该引用为null。
不像MethodInfo类型那样,可以不管底层方法的签名,委托对象必须属于一个与底层方法签名相关的委托类型。如图6.7所示,委托娄型总是直接派生于System.MulticastDelegate类型,而System.Multicast-Delegate类型又是派生于System.Delegate类型。这两个基类型提供了多个基本函数,同时也通知CLR,该类型实际上是一个委托类型。
像其它的CLR类型一样,委托类型有类型名,并且可以有成员,但是,委托类型的成员被限制为带有固定名字的有限方法集合,在这些方法中最重要的是Invoke方法。
Invoke方法必须是一个公有实例方法,此外,Invoke方法必须被标记为runtime,这意味着CLR将合成它的实现,而不是从类型模块的CIL中实时编译它,尽管名字和元数据特性是密不可分的,但实际的方法签名可以是任何CLR兼容的签名,Invoke方法的签名决定了委托类型可以如何使用,特别是,绑定到委托的任何方法都必须有与该委托的Invoke方法一致的签名。CLR将在编译时和运行时强制实施这种签名的匹配。
除了Invoke方法之外,委托类型必须提供一个接收两个参数的实例构造函数方法,第一个参数是一个System.Object类型,指定了要被绑定的目标对象引用.第二个参数是一个System.Intper类型,必须指向被绑定方法的代码,和Invoke方法一样,构造函数必须被标记为runtime,因为CLR将在运行合成它的实现。
每种编程语言都提供了其自身的语法,用于定义委托类型。C#、C++和VB.NET都共享一个相似的语法,它看起来像一个方法声明,而实际上是一个类型声明语句。看看下面的C#语句:
Public delegate int AddProc(int x,int y);
这条语句定义了个名为AddProc的新委托类型,其Invoke方法将接收两个System.Int32作为参数,并返回一个Sysrem.Int32作为结果。下面是对应于这个c#类型声明的ILASM:
.class public auto ansi sealed AddProc extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname instance void .ctor(object ‘object’,native int ‘method’) runtime managed
{}
.method public hidebysig virtual
Instance int32 Invoke(int32 x,int32y) runtime managed
{}
}
如同刚才所描述的,Invoke方法签名对应于用C#编写的类型定义语句。;