C#编程语言处理委托调用时有一点奇怪。C#程序不能通过名字显式地访问Invoke方法。更进一步说,省略了Invoke名,就导致一种类似C风格函数指针的使用模型:
static void Main(){
BinaryOp Op1 =new BinaryOp(MathCode.Subtract);
Int x=op1(3,4); //隐式地调用invoke
}
我认为,这种做法对委托几乎没有增加什么可用性。幸运的是,C++和VB.NET允许开发人员显式地使用Invoke方法。
Invoke的CLR实现支持多路委托链(chain),因此,单个Invoke调用可以同时触发多个方法调用。如图6.10所示,System.Multi-castDelegate类型增加了将多个委托对象链接到单个列表的支持。当你在列表头发出一个调用时,CLR合成的代码将依次遍历这个列表,并在列表的每个委托上调用目标方法。因为这些调用是依次发出的,所以,由方法对按引用传递(pass-by-reference)参数所做的任何修改,对于委托链中的下一个目标都是可见的。此外,如果Invoke方法返回一个类型化的值,则将只有最后一个方法的值返回给调用方。最后,当这些方法中的任何一个抛出异常时,调用便会停止在该点上,并将异常抛给调用方,System.Delegate类型支持两个方法用于管理委托链:Combine和Remove。
Namespce System{
Public abstract Delegate:Icloneable,Iserializable{
Static public Delegate Combine(Delegate a,Delegate b);
Static public Delegate Combine(Delegate[] delegates);
Static public Delegate Remeove(Delegate src,Delegate node);
}
}
这些方法都返回—个新的委托引用,指向更新后的委托链。这个引用可能指向(也可能不指向)作为参数传递的委托。
示例6.9是一个例子,它使用Delegate.Combine将两个委托组台到一个链(chain)中。注意,委托组合的顺序是很重要的,因为Invoke方法将按这个顺序遍历这个委托链。
示例6.9使用组合委托
Public delegate void MYPROC();
Public sealed class Util{
Public static void f(MYPROC first,MYPROC second){
Delegate pair=Delegate.Combine(first,second);
MYPROC tpair=(MYPROC)pair;
Tpair();// ccalls first() followed by second()
}
}
改变委托链的调用方式也是可能的,System.Delegate类型提供一个方法(GetInvocationList),它把委托链中的所有委托作为一个数组返回。如果能够访问这个数组,则可以精确地确定某个调用。示例6.10展示了一个向后遍历委托列表的例子。这个例子还展示了每个调用个体的中间结果。在本示例中,将得到每次调用结果的平均值。
示例6.10 使用组合委托(回顾)
Public delegate double MYPROC();
Public sealed class Util{
Public static double f(MYPROC first,MYPROC second){
//组合first(第一个委托)和second(第二个委托)
Delegate pair=Delegate.Combine(first,second);
//获取被组合的委托列表
Delegate[] targets=pair.GetInvocationList();
//遍历列表(逆序),并且返回平均值
Double total=0;
For(int i=targets.Length-1;i >=0;--i){
MyPROC target=(MYPROC)targets[i];
Total+=target();
}
Return total,targets.Length;
}
}