分析问题
在前文中已经介绍了链式委托的基本特性是一个以委托组成的链表,而当委托链上的任何一个委托方法被调用时,其后面的所有委托方法将会被依次顺序调用。那读者可能会产生这样的疑问,委托链上的原始顺序是如何形成的呢?回顾一下之前的代码,我们是如何生成一个链式委托的:
//申明一个委托变量,并绑定第一个方法 TestMulticastDelegate handler = new TestMulticastDelegate(PrintMessage1); //绑定第二个方法 handler += new TestMulticastDelegate(PrintMessage2); //绑定第三个方法 handler += new TestMulticastDelegate(PrintMessage3); //检查结果 handler();
相信这里的+=是造成困惑的主要原因,即便把它替换成TestMulticastDelegate handler=new TestMulticastDelegate(PrintMessage1)+new TestMulticastDelegate(PrintMessage2);读者可能仍然很难分辨究竟第一个委托被排在了链表的前面,还是第二个委托被排在了前面。C#语言的设计师们在重写委托的+时,确实没有考虑到在数学意义上加号左右参数的可交换性。为了清除起见,来看一下下面的代码:
TestMulticastDelegate handler1 = new TestMulticastDelegate(PrintMessage1); TestMulticastDelegate handler2 = new TestMulticastDelegate(PrintMessage2); Delegate d = System.MulticastDelegate.Combine(handler1, handler2); d.DynamicInvoke();
不错,这段代码本质上和第一段代码完全相同,并且更容易理解。Delegate MulticastDelegate.Combine(Delegate a,Delegate b)函数将串联两个委托,并且把第一个委托放在第二个委托之前,读者可以把两个委托的相加理解为Delegate MulticastDelegate.Combine(Delegate a,Delegate b)的调用。至此,链式委托的执行顺序及其由来就非常清晰了。
接下来,为了更彻底地理解链式委托的实现机制,有必要来看一下System.MulticastDelegate的内部成员,下图展示了System.MulticastDelegate中三个重要的内部成员。
在本节前文中已经介绍了System.Delegate的两个内部成员:_target和_methodPtr。System.MulticastDelegate继承了这两个成员,并且添加了一个_prev成员,该成员是一个委托的引用变量,当某个委托被串联到当前委托的后面时,该成员会被设置指向那个后续委托实例对象。.NET就依靠这一引用来逐一找到当前委托的所有后续委托并依次执行。
回到本节开始所提到的问题,程序员是否有能力控制链式委托的执行顺勋呢?读者可能会脱口而出,只要在定义链式委托时小心地按照需求希望的顺序来依次添加委托就可以了,完全正确,但如果在定义完链式委托后,突然希望改变执行顺序呢?又或者,程序需要按照实际运行情况来决定链式委托的执行顺序呢?答案就是,程序员需要手动地来做这个工作。来看以下代码:
TestMulticastDelegate hanlder = new TestMulticastDelegate(PrintMessage1); hanlder += new TestMulticastDelegate(PrintMessage2); Delegate[] h = hanlder.GetInvocationList();
该程序调用了定义在System.MulticastDelegate中的GetInvocationList(),用以获得整个链式委托中的所有委托。这样程序员就可以以任何希望的顺序来执行链式委托了。一个需要读者注意的地方是:Delegate[] GetInvocationList()是一个实例方法,所以其返回的是当前委托在委托链上的所有后续委托,而挂在该委托之前的委托(如果存在的话),程序员将不会也没有必要得到。
答案
链式委托的执行顺序是:按照委托链上的顺序从当前委托开始依次往后执行。如果有需要,可以通过GetInvocationList()方法来获得委托链上的所有需要执行的委托,并且按照任何希望的顺序去执行它们。