注意到Delegate是EventHandler的基类,所以为了触发事件,先要进行一个向下的强制转换,之后才能在其上触发事件,调用所有注册对象的方法。除了使用这种方式以外,还有一种更灵活方式可以调用方法,它是定义在Delegate基类中的DynamicInvoke()方法:
public object DynamicInvoke(params object[] args);
这可能是调用委托最通用的方法了,适用于所有类型的委托。它接受的参数为object[],也就是说它可以将任意数量的任意类型作为参数,并返回单个object对象。上面的DoSomething()方法也可以改写成下面这种通用形式:
public void DoSomething() { // 做某些其他的事情 if (MyEvent != null) { Delegate[] delArray = MyEvent.GetInvocationList(); foreach (Delegate del in delArray) { try { // 使用DynamicInvoke方法触发事件 del.DynamicInvoke(this, EventArgs.Empty); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.Message); } } } }
注意现在在DoSomething()方法中,我们取消了向具体委托类型的向下转换,现在没有了任何的基于特定委托类型的代码,而DynamicInvoke又可以接受任何类型的参数,且返回一个object对象。所以我们完全可以将DoSomething()方法抽象出来,使它成为一个公共方法,然后供其他类来调用,我们将这个方法声明为静态的,然后定义在Program类中:
// 触发某个事件,以列表形式返回所有方法的返回值 public static object[] FireEvent(Delegate del, params object[] args){ List<object> objList = new List<object>(); if (del != null) { Delegate[] delArray = del.GetInvocationList(); foreach (Delegate method in delArray) { try { // 使用DynamicInvoke方法触发事件 object obj = method.DynamicInvoke(args); if (obj != null) objList.Add(obj); } catch { } } } return objList.ToArray(); }
随后,我们在DoSomething()中只要简单的调用一下这个方法就可以了:
public void DoSomething() { // 做某些其他的事情 Program5.FireEvent(MyEvent, this, EventArgs.Empty); }
注意FireEvent()方法还可以返回一个object[]数组,这个数组包括了所有订阅者方法的返回值。而在上面的例子中,我没有演示如何获取并使用这个数组,为了节省篇幅,这里也不再赘述了,在本文附带的代码中,有关于这部分的演示,有兴趣的朋友可以下载下来看看。
委托中订阅者方法超时的处理
订阅者除了可以通过异常的方式来影响发布者以外,还可以通过另一种方式:超时。一般说超时,指的是方法的执行超过某个指定的时间,而这里我将含义扩展了一下,凡是方法执行的时间比较长,我就认为它超时了,这个“比较长”是一个比较模糊的概念,2秒、3秒、5秒都可以视为超时。超时和异常的区别就是超时并不会影响事件的正确触发和程序的正常运行,却会导致事件触发后需要很长才能够结束。在依次执行订阅者的方法这段期间内,客户端程序会被中断,什么也不能做。因为当执行订阅者方法时(通过委托,相当于依次调用所有注册了的方法),当前线程会转去执行方法中的代码,调用方法的客户端会被中断,只有当方法执行完毕并返回时,控制权才会回到客户端,从而继续执行下面的代码。我们来看一下下面一个例子:
class Program6 { static void Main(string[] args) { Publisher pub = new Publisher(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.MyEvent += new EventHandler(sub1.OnEvent); pub.MyEvent += new EventHandler(sub2.OnEvent); pub.MyEvent += new EventHandler(sub3.OnEvent); pub.DoSomething(); // 触发事件 Console.WriteLine("\nControl back to client!"); // 返回控制权 } // 触发某个事件,以列表形式返回所有方法的返回值 public static object[] FireEvent(Delegate del, params object[] args) { // 代码与上同,略 } } public class Publisher { public event EventHandler MyEvent; public void DoSomething() { // 做某些其他的事情 Console.WriteLine("DoSomething invoked!"); Program6.FireEvent(MyEvent, this, EventArgs.Empty); //触发事件 } } public class Subscriber1 { public void OnEvent(object sender, EventArgs e) { Thread.Sleep(TimeSpan.FromSeconds(3)); Console.WriteLine("Waited for 3 seconds, subscriber1 invoked!"); } } public class Subscriber2 { public void OnEvent(object sender, EventArgs e) { Console.WriteLine("Subscriber2 immediately Invoked!"); } } public class Subscriber3 { public void OnEvent(object sender, EventArgs e) { Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine("Waited for 2 seconds, subscriber2 invoked!"); } }