15委托和事件中注册方法的返回值

当委托有返回值,事件注册了多个方法后,触发事件,到底是哪个注册方法返回值呢?

模拟"我是歌手"采访观众,把"我是歌手"看作被监视对象,把观众看作是观察者Observer。

 

  让最后一个注册方法返回值

   1:  namespace ConsoleApplication15
   2:  {
   3:      class Program
   4:      {
   5:          static void Main(string[] args)
   6:          {
   7:              ImSinger singer = new ImSinger();
   8:              Audience1 a1 = new Audience1();
   9:              Audience2 a2 = new Audience2();
  10:              Audience3 a3 = new Audience3();
  11:   
  12:              singer.CommentChanged += new CommentEventHandler(a1.OnCommentChanged);
  13:              singer.CommentChanged += new CommentEventHandler(a2.OnCommentChanged);
  14:              singer.CommentChanged += new CommentEventHandler(a3.OnCommentChanged);
  15:   
  16:              Console.WriteLine("主持人:您觉得本场我是歌手谁的表现最好?");
  17:              singer.Interview();
  18:              Console.ReadKey();
  19:          }
  20:      }
  21:   
  22:      //定义委托
  23:      public delegate string CommentEventHandler();
  24:   
  25:      public class ImSinger
  26:      {
  27:          public event CommentEventHandler CommentChanged;
  28:   
  29:          public void Interview()
  30:          {
  31:              if (CommentChanged != null)
  32:              {
  33:                  string rtn = CommentChanged();
  34:                  Console.WriteLine(rtn);
  35:              }
  36:          }
  37:      }
  38:   
  39:      public class Audience1
  40:      {
  41:          public string OnCommentChanged()
  42:          {
  43:              return "观众1:我觉得周笔畅发挥最好!";
  44:          }
  45:      }
  46:   
  47:      public class Audience2
  48:      {
  49:          public string OnCommentChanged()
  50:          {
  51:              return "观众2:我觉得张杰发挥最好!";
  52:          }
  53:      }
  54:      public class Audience3
  55:      {
  56:          public string OnCommentChanged()
  57:          {
  58:              return "观众3:我觉得邓紫棋发挥最好!";
  59:          }
  60:      }
  61:  }
  62:   

 

结果:

1

 

可见,最后一个注册方法返回了值。如果导演想任意控制哪个注册方法起效呢?

 

  让任意注册方法返回值

● 可以设计一个"事件访问器Event Accessor",可以像属性一样给委托变量赋值,或读取委托变量的值。
● 而且还要设计一个私有的委托变量来配合事件访问器

   1:          private CommentEventHandler commentChanged;
   2:          public event CommentEventHandler CommentChanged
   3:          {
   4:              add { commentChanged = value; }
   5:              remove { commentChanged -= value; }
   6:          }

完整如下:

   1:  namespace ConsoleApplication15
   2:  {
   3:      class Program
   4:      {
   5:          static void Main(string[] args)
   6:          {
   7:              ImSinger singer = new ImSinger();
   8:              Audience1 a1 = new Audience1();
   9:              Audience2 a2 = new Audience2();
  10:              Audience3 a3 = new Audience3();
  11:   
  12:              singer.CommentChanged -= a1.OnCommentChanged;
  13:              singer.CommentChanged += a2.OnCommentChanged;
  14:              singer.CommentChanged += a1.OnCommentChanged;
  15:   
  16:              Console.WriteLine("主持人:您觉得本场我是歌手谁的表现最好?");
  17:              singer.Interview();
  18:              Console.ReadKey();
  19:          }
  20:      }
  21:   
  22:      //定义委托
  23:      public delegate string CommentEventHandler();
  24:   
  25:      public class ImSinger
  26:      {
  27:          private CommentEventHandler commentChanged;
  28:          public event CommentEventHandler CommentChanged
  29:          {
  30:              add { commentChanged = value; }
  31:              remove { commentChanged -= value; }
  32:          }
  33:   
  34:          public void Interview()
  35:          {
  36:              if (commentChanged != null)
  37:              {
  38:                  string rtn = commentChanged();
  39:                  Console.WriteLine(rtn);
  40:              }
  41:          }
  42:      }
  43:   
  44:      public class Audience1
  45:      {
  46:          public string OnCommentChanged()
  47:          {
  48:              return "观众1:我觉得周笔畅发挥最好!";
  49:          }
  50:      }
  51:   
  52:      public class Audience2
  53:      {
  54:          public string OnCommentChanged()
  55:          {
  56:              return "观众2:我觉得张杰发挥最好!";
  57:          }
  58:      }
  59:      public class Audience3
  60:      {
  61:          public string OnCommentChanged()
  62:          {
  63:              return "观众3:我觉得邓紫棋发挥最好!";
  64:          }
  65:      }
  66:  }
  67:   

 

结果:
2

 

可见,通过事件访问器可以任意注册和注销方法。

 

  获取多个注册方法的多个返回值

委托的基类Delegate维护了一个委托链表,每行存储着一个注册方法,可以通过基类的GetInvocationList()获得这个委托链表。然后遍历委托链表,进行向下转换成自定义委托,执行方法。

   1:  namespace ConsoleApplication15
   2:  {
   3:      class Program
   4:      {
   5:          static void Main(string[] args)
   6:          {
   7:              ImSinger singer = new ImSinger();
   8:              Audience1 a1 = new Audience1();
   9:              Audience2 a2 = new Audience2();
  10:              Audience3 a3 = new Audience3();
  11:   
  12:              singer.CommentChanged += new CommentEventHandler(a1.OnCommentChanged);
  13:              singer.CommentChanged += new CommentEventHandler(a2.OnCommentChanged);
  14:              singer.CommentChanged += new CommentEventHandler(a3.OnCommentChanged);
  15:   
  16:              Console.WriteLine("主持人:您觉得本场我是歌手谁的表现最好?");
  17:              List<string> list = singer.Interview();
  18:              foreach (string s in list)
  19:              {
  20:                  Console.WriteLine(s);
  21:              }
  22:              Console.ReadKey();
  23:          }
  24:      }
  25:   
  26:      //定义委托
  27:      public delegate string CommentEventHandler();
  28:   
  29:      public class ImSinger
  30:      {
  31:          public event CommentEventHandler CommentChanged;
  32:   
  33:          public List<string> Interview()
  34:          {
  35:              List<string> strList = new List<string>();
  36:              if (CommentChanged == null) return strList;
  37:   
  38:              Delegate[] delArray = CommentChanged.GetInvocationList();
  39:              foreach (Delegate del in delArray)
  40:              {
  41:                  CommentEventHandler method = (CommentEventHandler) del; //向下转换
  42:                  strList.Add(method());
  43:              }
  44:              return strList;
  45:          }
  46:      }
  47:   
  48:      public class Audience1
  49:      {
  50:          public string OnCommentChanged()
  51:          {
  52:              return "观众1:我觉得周笔畅发挥最好!";
  53:          }
  54:      }
  55:   
  56:      public class Audience2
  57:      {
  58:          public string OnCommentChanged()
  59:          {
  60:              return "观众2:我觉得张杰发挥最好!";
  61:          }
  62:      }
  63:      public class Audience3
  64:      {
  65:          public string OnCommentChanged()
  66:          {
  67:              return "观众3:我觉得邓紫棋发挥最好!";
  68:          }
  69:      }
  70:  }
  71:   

 

结果:
3

 

  订阅者方法出现异常如何处理

What:
订阅者方法可能会抛出异常。

 

Why:
会使发布者程序终止,而出现异常的订阅者后面的订阅者方法将不会被执行。

how:
我们要针对订阅者方法可能出现的异常,进行异常处理。

 

□ 问题呈现:让一个订阅者方法出现异常,后续注册方法不能被执行

   1:  namespace ConsoleApplication15
   2:  {
   3:      class Program
   4:      {
   5:          static void Main(string[] args)
   6:          {
   7:              ImSinger singer = new ImSinger();
   8:              Audience1 a1 = new Audience1();
   9:              Audience2 a2 = new Audience2();
  10:              Audience3 a3 = new Audience3();
  11:   
  12:              singer.CommentChanged += new CommentEventHandler(a1.OnCommentChanged);
  13:              singer.CommentChanged += new CommentEventHandler(a2.OnCommentChanged);
  14:              singer.CommentChanged += new CommentEventHandler(a3.OnCommentChanged);
  15:   
  16:              Console.WriteLine("主持人:您觉得本场我是歌手谁的表现最好?");
  17:              singer.Interview();
  18:              Console.ReadKey();
  19:          }
  20:      }
  21:   
  22:      //定义委托
  23:      public delegate void CommentEventHandler();
  24:   
  25:      public class ImSinger
  26:      {
  27:          public event CommentEventHandler CommentChanged;
  28:   
  29:          public void Interview()
  30:          {
  31:              if (CommentChanged != null)
  32:              {
  33:                  try
  34:                  {
  35:                      CommentChanged();
  36:                  }
  37:                  catch (Exception e)
  38:                  {
  39:                      
  40:                      Console.WriteLine("有订阅者方法出现异常了:" +e.Message);
  41:                  }
  42:              }
  43:          }
  44:      }
  45:   
  46:      public class Audience1
  47:      {
  48:          public void OnCommentChanged()
  49:          {
  50:              Console.WriteLine("观众1:我觉得周笔畅发挥最好!");
  51:          }
  52:      }
  53:   
  54:      public class Audience2
  55:      {
  56:          public void OnCommentChanged()
  57:          {
  58:              throw new Exception("我是2号观众,对不起,我卡了");  
  59:          }
  60:      }
  61:      public class Audience3
  62:      {
  63:          public void OnCommentChanged()
  64:          {
  65:              Console.WriteLine("观众3:我觉得邓紫棋发挥最好!");
  66:          }
  67:      }
  68:  }
  69:   

 

结果:
4

 

可见,订阅者Audience2的方法出现异常,导致后面的订阅者Audience3方法无法被执行。

 

□ 在委托链表中处理异常,一个订阅者方法出现异常,后续注册方法依然能被执行

what:
在委托链中处理异常,一旦订阅者的注册方法抛异常,也不会影响后续订阅者的注册方法。

 

why:
因为要遍历委托链中的某个委托,即注册方法,当一个注册方法抛异常后,还会遍历后续的注册方法。

   1:  namespace ConsoleApplication15
   2:  {
   3:      class Program
   4:      {
   5:          static void Main(string[] args)
   6:          {
   7:              ImSinger singer = new ImSinger();
   8:              Audience1 a1 = new Audience1();
   9:              Audience2 a2 = new Audience2();
  10:              Audience3 a3 = new Audience3();
  11:   
  12:              singer.CommentChanged += new CommentEventHandler(a1.OnCommentChanged);
  13:              singer.CommentChanged += new CommentEventHandler(a2.OnCommentChanged);
  14:              singer.CommentChanged += new CommentEventHandler(a3.OnCommentChanged);
  15:   
  16:              Console.WriteLine("主持人:您觉得本场我是歌手谁的表现最好?");
  17:              singer.Interview();
  18:              Console.ReadKey();
  19:          }
  20:      }
  21:   
  22:      //定义委托
  23:      public delegate void CommentEventHandler();
  24:   
  25:      public class ImSinger
  26:      {
  27:          public event CommentEventHandler CommentChanged;
  28:   
  29:          public void Interview()
  30:          {
  31:              if (CommentChanged != null)
  32:              {
  33:                  Delegate[] delArray = CommentChanged.GetInvocationList();
  34:                  foreach (Delegate del in delArray)
  35:                  {
  36:                      CommentEventHandler method = (CommentEventHandler)del;
  37:                      try
  38:                      {
  39:                          method();
  40:                      }
  41:                      catch (Exception e)
  42:                      {
  43:                          Console.WriteLine("订阅者方法有异常:" + e.Message);
  44:                      }
  45:                  }
  46:              }
  47:          }
  48:      }
  49:   
  50:      public class Audience1
  51:      {
  52:          public void OnCommentChanged()
  53:          {
  54:              Console.WriteLine("观众1:我觉得周笔畅发挥最好!");
  55:          }
  56:      }
  57:   
  58:      public class Audience2
  59:      {
  60:          public void OnCommentChanged()
  61:          {
  62:              throw new Exception("我是2号观众,对不起,我卡了");  
  63:          }
  64:      }
  65:      public class Audience3
  66:      {
  67:          public void OnCommentChanged()
  68:          {
  69:              Console.WriteLine("观众3:我觉得邓紫棋发挥最好!");
  70:          }
  71:      }
  72:  }
  73:   

 

结果:
5

 

改进:
CommentEventHandler method = (CommentEventHandler)del;这里有一个强制向下转换,可以用DynamicInvoke()方法替代。

   1:     //定义委托
   2:      public delegate void CommentEventHandler();
   3:   
   4:      public class ImSinger
   5:      {
   6:          public event CommentEventHandler CommentChanged;
   7:   
   8:          public void Interview()
   9:          {
  10:              if (CommentChanged != null)
  11:              {
  12:                  Delegate[] delArray = CommentChanged.GetInvocationList();
  13:                  foreach (Delegate del in delArray)
  14:                  {
  15:                      try
  16:                      {
  17:                          del.DynamicInvoke();
  18:                      }
  19:                      catch (Exception e)
  20:                      {
  21:                          Console.WriteLine("订阅者方法有异常:" + e.Message);
  22:                      }
  23:                  }
  24:              }
  25:          }
  26:      }
  27:   

 

但使用DynamicInvoke()不好的地方在于:
一旦订阅者方法抛异常,程序就中断了,并抛出异常。
6

 

参考资料:
《.NET之美》--张子阳,感谢!

原文地址:https://www.cnblogs.com/darrenji/p/3615438.html