12事件


继续上一篇:11委托演绎中国足球队打进世界杯  


□ 含有相同参数列表的各种处理球方法不变,把带委托参数的方法放到一个类中

   1:  namespace ConsoleApplication11
   2:  {
   3:      class Program
   4:      {
   5:          static void Main(string[] args)
   6:          {
   7:              GameManager gm = new GameManager();
   8:              gm.Game("球",PassBall);
   9:              Console.WriteLine();
  10:              gm.Game("球",TransferpBall);
  11:              Console.ReadKey();
  12:          }
  13:   
  14:          private static void PassBall(string ball)
  15:          {
  16:              Console.Write("传" + ball);
  17:          }
  18:   
  19:          private static void StopBall(string ball)
  20:          {
  21:              Console.Write("停/接" + ball);
  22:          }
  23:   
  24:          private static void TransferpBall(string ball) 
  25:          {
  26:              Console.Write("转移" + ball);
  27:          }
  28:   
  29:          private static void RunBall(string ball)
  30:          {
  31:              Console.Write("带" + ball);
  32:          }
  33:   
  34:          private static void ScoreBallByHead(string ball)
  35:          {
  36:              Console.Write("头"+ball + "射门");
  37:          }
  38:   
  39:          private static void ScoreBallByInnerFeet(string ball)
  40:          {
  41:              Console.Write("推射" + ball + "射门");
  42:          }
  43:   
  44:          private static void ScoreBallByOuterFeet(string ball)
  45:          {
  46:              Console.Write("外脚背射" + ball + "门");
  47:          }
  48:      }
  49:      public delegate void PlayBall(string ball);
  50:   
  51:      public class GameManager
  52:      {
  53:          public void Game(string ball, PlayBall playBallDelegate)
  54:          {
  55:              playBallDelegate(ball);
  56:          }
  57:      }
  58:  }

 

结果:
2

 

□ 采用多播委托,把多个方法绑定到同一个委托变量

   1:              GameManager gm = new GameManager();
   2:              PlayBall del = PassBall;
   3:              del += RunBall;
   4:              gm.Game("球",del);
   5:              Console.ReadKey();

 

当然也可以:
3


□ 把委托封装到类中

   1:      class Program
   2:      {
   3:          static void Main(string[] args)
   4:          {
   5:              GameManager gm = new GameManager();
   6:              gm.delegate1 = ScoreBallByHead;
   7:              gm.delegate1 += ScoreBallByInnerFeet;
   8:              gm.Game("球", gm.delegate1);
   9:              Console.ReadKey();
  10:          }
  11:          .....
  12:      }
  13:      public delegate void PlayBall(string ball);
  14:   
  15:      public class GameManager
  16:      {
  17:          public PlayBall delegate1;
  18:          public void Game(string ball, PlayBall playBallDelegate)
  19:          {
  20:              playBallDelegate(ball);
  21:          }
  22:      }

 

结果:
4

 

可以把gm.Game("球", gm.delegate1)改成类似gm.Game("球"),达到这样的效果,需要在Game()方法体内让委托变量来执行方法。

□ 直接调用类的实例方法,不带委托参数

   1:     class Program
   2:      {
   3:          static void Main(string[] args)
   4:          {
   5:              GameManager gm = new GameManager();
   6:              gm.delegate1 = PassBall;
   7:              gm.delegate1 += ScoreBallByOuterFeet;
   8:              gm.Game("球");
   9:              Console.ReadKey();
  10:          }
  11:          ......
  12:      }
  13:      public delegate void PlayBall(string ball);
  14:   
  15:      public class GameManager
  16:      {
  17:          public PlayBall delegate1;
  18:          public void Game(string ball)
  19:          {
  20:              if (delegate1 != null)
  21:              {
  22:                  delegate1(ball);
  23:              }
  24:          }
  25:      }
  26:   

 

结果:
5

 

依然存在的问题是:
1、把委托字段成delegate1声明成public,客户端可以随意赋值,破坏了对象的封闭原则。
2、如果把委托字段成delegate1声明成private,客户端就不能调用。
3、给委托赋值用=,给委托注册用+=,好像也不是很统一。

 

□ 事件就此登上历史舞台

   1:      class Program
   2:      {
   3:          static void Main(string[] args)
   4:          {
   5:              GameManager gm = new GameManager();
   6:              gm.event1 = PassBall;
   7:              gm.event1 += ScoreBallByOuterFeet;
   8:              gm.Game("球");
   9:              Console.ReadKey();
  10:          }
  11:          ......
  12:      }
  13:      public delegate void PlayBall(string ball);
  14:   
  15:      public class GameManager
  16:      {
  17:          public event PlayBall event1;
  18:          public void Game(string ball)
  19:          {
  20:              event1(ball);
  21:          }
  22:      }
  23:   

 

事件可以看成:
为委托类型量身定制的属性。

编译时就报错,运行也报错:
6

 

使用事件的好处:
限制含有事件类型的能力,即事件应该由事件的发布者触发,而不应该由事件的客户端来触发。

 

□ 事件的一个例子

   1:  namespace ConsoleApplication12
   2:  {
   3:      class Program
   4:      {
   5:          static void Main(string[] args)
   6:          {
   7:              Publisher publisher = new Publisher(2);
   8:              Subscriber subscriber = new Subscriber();
   9:   
  10:              publisher.NumberChanged += new NumberChangedEventHandler(subscriber.OnNumberChanged);
  11:              publisher.DoSth();
  12:              Console.ReadKey();
  13:          }
  14:      }
  15:   
  16:      //定义委托
  17:      public delegate void NumberChangedEventHandler(int count);
  18:   
  19:      //事件发布者
  20:      public class Publisher
  21:      {
  22:          private int count;
  23:          public event NumberChangedEventHandler NumberChanged;
  24:   
  25:          public Publisher(int count)
  26:          {
  27:              this.count = count;
  28:          }
  29:   
  30:          public void DoSth()
  31:          {
  32:              Console.WriteLine("我是发布者,我开始工作了~~");
  33:              if (NumberChanged != null)
  34:              {
  35:                  count++;
  36:                  NumberChanged(count);
  37:              }
  38:          }
  39:      }
  40:   
  41:      //定义事件订阅者
  42:      public class Subscriber
  43:      {
  44:          public void OnNumberChanged(int count)
  45:          {
  46:              Console.WriteLine("我知道了,现在的数量是:" + count);
  47:          }
  48:      }
  49:  }
  50:   


结果:
7

 

关于事件的小结:
● 在委托的前面加上关键字event,就变成了事件public event NumberChangedEventHandler NumberChanged
● 一旦声明成事件,只能通过事件所在类(发布者)的方法来触发事件:publisher.DoSth();

 

订阅者的和发布者的惯例:
● 如果想运行订阅者的方法触发事件public delegate void NumberChangedEventHandler(int count),从而让订阅者也触发相应的方法,那订阅者的方法OnNumberChanged(int count)的参数列表和事件一致,并且是在事件变量NumberChanged前面加上On变成OnNumberChanged。

● 订阅者是通过publisher.NumberChanged += new NumberChangedEventHandler(subscriber.OnNumberChanged)绑定到发布者的事件,建立触发关联的。

 

参考资料:
《.NET之美》--张子阳,感谢写了这么好的书!

posted @ 2014-03-18 01:55  Darren Ji  阅读(403)  评论(0编辑  收藏  举报

我的公众号:新语新世界,欢迎关注。