为了实例化一个委托,需要一个方法和一个可选的目标对象引用,只有在绑定到一个实例方法时,才需要目标对象引用。当绑定到一个静态方法时,这是不需要的。System.Delegate类型提供一个createDelegate静态方法,用于创建一个新的委托,以绑定一个特定方法和对象。下面是CreateDelegate的四种重载形式:
namespace System{
public abstract class Delegate{
//绑定一个类型dt的委托到方法上
public static Delegate
CreateDelegate(Type dt,MethodInfo method);
//绑定一个类型dt的委托到目标类型的methName方法上
public static Delegate
CreateDelegate(Type dt,Type target,string methName);
//绑定一个类型dt的委托到目标对象的methName方法上
public static Delegate
CreateDelegate(Type dt,Object target,string methName);
//绑定一个类型dt的委托到目标对象的methName方法上
public static Delegate
CreateDelegate(Type dt,Object target,string methName,bool ignoreCase);
//为了清晰起见,省略剩余部分
}
}
第一对重载形式用于绑定一个新的委托对象到静态方法上。第二对用于在特定对象上绑定实例方法。在所有这些情形中,第一个参数是一个System.Type对象,它描述期望的委托类型。指定的目标方法必须确切地匹配委托类型的Invoke方法签名。
下面的c#代码使用CreateDelegate方法,将委托绑定到一个静态方法和一个实例方法上。
using System;
public delegate int BinaryOp(int x,int y);
public class MathCode{
internal int sum=0;
public int Add(int m,int n){
sum+=m+n;
return m+n;
}
public static int Subtract(int a,int b)
{
return a-b;
}
class app{
static void Main()
{\MathjCode target=new MathCode();
Type tt=typeof(MathCode);
Type dt=typeof(BInaryOp);
BinaryOp op1=(BinaryOp)Delegate.CreateDelegate(dt,tt,”Subtract”);
BinaryOp op2=(BinaryOp)Delegate.CreateDelegate(dt,target,”Add”);
}
}
}
调用CreateDelegate是调用委托类型构造函数的一种间接方法。每种编程语言都提供了自身的语法,用于直接调用构造函数,对于c#而言,可以简单地通过类型名字或对象引用限定,以指定方法的符号化名字。下面的Main方法等价于前面的示例:
static void Main() {
MathCode target=new MathCoce();l
BinaryOp op1=new BinaryOp(MathCode.Subtract);
BinaryOp op2=new BinaryOp(target.Add);
}
在调用委托类型的构造函数之前,c#编译器将转换这些new表达式,使用底层CIL的ldftn或ldvirtftn操作符获取目标方法的地址。这个技术用于绑定委托比调用Delegate.CreateDelegate要快得多,原因就是不需要通过元数据遍历查找方法句柄。
在CLR实例化委托并将它绑定到方法和对象后,其主要目标就是支持调用。你可以用两种方式使用委托进行调用。如果你需要一个通用机制(接MethodInfo.invoke的方式),System.Delegate类型提供了一个DynamicInvoke方法:
Namespace System{
Public abstract class Delegate{
Public object DynamicInvoke(object[] args);
//为了清晰起见,省略剩余成员
}
}
注意,用于DynamicInvoke的签名与MethodInfo.Invoke的一样,除了目标对象的引用没有被显式地传递。准确地说,委托的target字段充当了调用的隐式目标,如图6.8所示。
对委托的调用更为通用的方式是:使用类型相关的Invoke方法。不像DynamicInvoke方法,Invoke方法是强类型的。由于不具有一般性,它的性能更好。用于IA-32的CLR合成(CLR-synthesized)的Invoke实现,只是一个8条指令的垫片(shim),它使用目标对象引用的指针替换ecx中的this指针。接着,该垫片直接jmp到目标方法地址。任jmp发生后,目标方法开始执行,就好像它是直接由调用方所调用的一样。实际上,因为调用方的返回地址仍然在堆栈上,目标方法将直接返回到调用方,这样便完全绕开了委托的方式。
由Invoke方法所使用的垫片可以普遍性地工作,原因是目标方法的签名被保证与Invoke方法的签名恰好匹配。如图6.9所示,当Invoke分发到目标方法时,你可以重用来自Invoke调用的堆栈帧。e