zoukankan      html  css  js  c++  java
  • 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之美》--张子阳,感谢!

  • 相关阅读:
    hiho_1081_最短路径1
    hiho_1079_离散化
    hiho_1078_线段树区间修改
    hiho_1069_最近公共祖先3
    【.netcore学习】.netcore添加到 supervisor 守护进程自启动报错
    【.NetCore学习】ubuntu16.04 搭建.net core mvc api 运行环境
    【.NetCore学习】ASP.NET Core EF Core2.0 DB First现有数据库自动生成实体Context
    【vue基础学习】vue.js开发环境搭建
    【vue学习】vue中怎么引用laydate.js日期插件
    【年终总结】个人的2017年年终总结
  • 原文地址:https://www.cnblogs.com/darrenji/p/3615438.html
Copyright © 2011-2022 走看看