现在假设我们想要获得多个订阅者的返回值,以List<string>的形式返回,该如何做呢?我们应该记得委托定义在编译时会生成一个继承自MulticastDelegate的类,而这个MulticastDelegate又继承自Delegate,在Delegate内部,维护了一个委托链表,链表上的每一个元素,为一个只包含一个目标方法的委托对象。而通过Delegate基类的GetInvocationList()静态方法,可以获得这个委托链表。随后我们遍历这个链表,通过链表中的每个委托对象来调用方法,这样就可以分别获得每个方法的返回值:
class Program4 { static void Main(string[] args) { Publishser pub = new Publishser(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.NumberChanged += new DemoEventHandler(sub1.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub2.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub3.OnNumberChanged); List<string> list = pub.DoSomething(); //调用方法,在方法内触发事件 foreach (string str in list) { Console.WriteLine(str); } } } public delegate string DemoEventHandler(int num); // 定义事件发布者 public class Publishser { public event DemoEventHandler NumberChanged; // 声明一个事件 public List<string> DoSomething() { // 做某些其他的事 List<string> strList = new List<string>(); if (NumberChanged == null) return strList; // 获得委托数组 Delegate[] delArray = NumberChanged.GetInvocationList(); foreach (Delegate del in delArray) { // 进行一个向下转换 DemoEventHandler method = (DemoEventHandler)del; strList.Add(method(100)); // 调用方法并获取返回值 } return strList; } } // 定义事件订阅者 public class Subscriber1 { public string OnNumberChanged(int num) { Console.WriteLine("Subscriber1 invoked, number:{0}", num); return "[Subscriber1 returned]"; } } public class Subscriber3 {与上面类同,略} public class Subscriber3 {与上面类同,略}
如果运行上面的代码,可以得到这样的输出:
复制代码 代码如下:
Subscriber1 invoked, number:100
Subscriber2 invoked, number:100
Subscriber3 invoked, number:100
[Subscriber1 returned]
[Subscriber2 returned]
[Subscriber3 returned]
可见我们获得了三个方法的返回值。而我们前面说过,很多情况下委托的定义都不包含返回值,所以上面介绍的方法似乎没有什么实际意义。其实通过这种方式来触发事件最常见的情况应该是在异常处理中,因为很有可能在触发事件时,订阅者的方法会抛出异常,而这一异常会直接影响到发布者,使得发布者程序中止,而后面订阅者的方法将不会被执行。因此我们需要加上异常处理,考虑下面一段程序:
class Program5 { static void Main(string[] args) { Publisher pub = new Publisher(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.NumberChanged += new DemoEventHandler(sub1.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub2.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub3.OnNumberChanged); } } public class Publisher { public event EventHandler MyEvent; public void DoSomething() { // 做某些其他的事情 if (MyEvent != null) { try { MyEvent(this, EventArgs.Empty); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.Message); } } } } public class Subscriber1 { public void OnEvent(object sender, EventArgs e) { Console.WriteLine("Subscriber1 Invoked!"); } } public class Subscriber2 { public void OnEvent(object sender, EventArgs e) { throw new Exception("Subscriber2 Failed"); } } public class Subscriber3 {/* 与Subsciber1类同,略*/}
注意到我们在Subscriber2中抛出了异常,同时我们在Publisher中使用了try/catch语句来处理异常。运行上面的代码,我们得到的结果是:
复制代码 代码如下:
Subscriber1 Invoked!
Exception: Subscriber2 Failed
可以看到,尽管我们捕获了异常,使得程序没有异常结束,但是却影响到了后面的订阅者,因为Subscriber3也订阅了事件,但是却没有收到事件通知(它的方法没有被调用)。此时,我们可以采用上面的办法,先获得委托链表,然后在遍历链表的循环中处理异常,我们只需要修改一下DoSomething方法就可以了:
public void DoSomething() { if (MyEvent != null) { Delegate[] delArray = MyEvent.GetInvocationList(); foreach (Delegate del in delArray) { EventHandler method = (EventHandler)del; // 强制转换为具体的委托类型 try { method(this, EventArgs.Empty); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.Message); } } } }