在所有关于C#事件机制的介绍中,我更倾向于发布者/订阅者(Publisher/Subscriber)这种描述。理解事件机制并不是一件容易的事情,它所涉及的思想值得我们好好去研究。


 


谈到事件,我们涉及到两个角色:事件发布者(Publisher)和事件订阅者(Scriber),也可以说是事件发送者(Sender)和事件接收者(Receiver)的关系。举个例子来说,市面上目前有许多杂志,杂志的种类也很多。而我只对其中的某些感兴趣,那么我就可以向杂志发行商提出订阅。之后,每当杂志发行时,我就会收到我在杂志发行商那儿订阅的杂志。在这个关系中,杂志发行商就相当于事件发行者,而我就是事件订阅者。每当杂志发行时,就触发了一个发行事件。

用面向对象的语言解释,这两者的意义如下:

事件发行者(Publisher)

它是一个对象,且会维护自身的状态信息。每当状态信息发生变动时,便触发一个事件,并通知所有的事件订阅者。对于杂志发行商来说,每本杂志都有自己的信息在里面,当杂志发行时,我要通知订阅该杂志的人:杂志已经发行啦,请注意查收!

事件接收者(Receiver)

这个对象要注册它感兴趣的对象,也就是订阅它自己喜欢的杂志啦。另外,这个对象通常要提供一个事件处理方法,在事件发行者触发一个事件后,会自动执行这个方法。对于上面所举的例子来说,也就是我收到杂志后要做什么事情,比如,你可以满世界地大喊:我收到杂志啦!也可以将杂志收藏起来慢慢欣赏,具体怎么实现完全取决你自己的喜好。

以下是.NET事件处理机制的模型:



C# 事件机制_c#



 下面给一个简单的例子,用以阐述事件的思想:



 1C# 事件机制_事件处理_02using System;

 2C# 事件机制_事件处理_02using System.Collections.Generic;

 3C# 事件机制_事件处理_02using System.Text;

 4C# 事件机制_事件处理_02

 5C# 事件机制_事件处理_02namespace EventDemo

 6C# 事件机制_c#_07{

 7C# 事件机制_c#_08       public delegate void SalaryCompute();        //声明一个代理类

 8C# 事件机制_c#_08

 9C# 事件机制_c#_08       public class Employee

10C# 事件机制_.net_11       {

11C# 事件机制_c#_08              public event SalaryCompute OnSalaryCompute;         //定义事件,将其与代理绑定

12C# 事件机制_c#_08

13C# 事件机制_c#_08              public virtual void FireEvent()       //触发事件的方法

14C# 事件机制_.net_11              {

15C# 事件机制_c#_08                     if (OnSalaryCompute != null)

16C# 事件机制_.net_11                     {

17C# 事件机制_c#_08                            OnSalaryCompute();      //触发事件

18C# 事件机制_代理类_19                     }

19C# 事件机制_代理类_19              }

20C# 事件机制_代理类_19       }

21C# 事件机制_c#_08

22C# 事件机制_c#_08       public class HumanResource

23C# 事件机制_.net_11       {

24C# 事件机制_c#_08              public void SalaryHandler()          //事件处理函数

25C# 事件机制_.net_11              {

26C# 事件机制_c#_08                     Console.WriteLine("SalaryC# 事件机制_事件机制_28C# 事件机制_事件机制_28");     //只是打印一行字而已

27C# 事件机制_代理类_19              }

28C# 事件机制_c#_08

29C# 事件机制_c#_08              public static void Main()

30C# 事件机制_.net_11              {

31C# 事件机制_c#_08                     Employee ep = new Employee();

32C# 事件机制_c#_08                     HumanResource hr = new HumanResource();

33C# 事件机制_c#_08                     ep.OnSalaryCompute+=new SalaryCompute(hr.SalaryHandler);       //注册

34C# 事件机制_c#_08                     ep.FireEvent();        //触发事件

35C# 事件机制_c#_08                     Console.Read();

36C# 事件机制_代理类_19              }

37C# 事件机制_代理类_19       }

38C# 事件机制_代理类_41}

39C# 事件机制_事件处理_02

在这个例子中,Employee类相当于一个事件发布者(Publisher),它定义了事件的相关信息,包括定义了一个事件用于计算薪水(OnSalaryCompute),以及一个触发事件的函数(FireEvent()),为简单起见,本例没有加上事件参数。


与之相对应,HumanResource类则相当于一个事件订阅者(Subscriber),它定义了一个事件处理函数(SalaryHandler()),并用+=将其与事件联系起来,从而使事件触发的时候能够调用我这个方法(在本例中也就是打印一行字啦)。值得注意的一点是,事件处理函数的方法签名要与代理的方法签名相同,这是非常重要的一点。 


下面将这个例子改造一下,事件参数信息,用以完善事件机制。


 1C# 事件机制_事件处理_02using System;

 2C# 事件机制_事件处理_02using System.Collections.Generic;

 3C# 事件机制_事件处理_02using System.Text;

 4C# 事件机制_事件处理_02using System.Threading;

 5C# 事件机制_事件处理_02

 6C# 事件机制_事件处理_02namespace EventDemo

 7C# 事件机制_c#_07{

 8C# 事件机制_c#_08       public delegate void SalaryCompute(object sender,MyEventArgs e);        //声明一个代理类

 9C# 事件机制_c#_08

10C# 事件机制_c#_08       public class Employee

11C# 事件机制_.net_11       {

12C# 事件机制_c#_08              public event SalaryCompute OnSalaryCompute;         //定义事件,将其与代理绑定

13C# 事件机制_c#_08

14C# 事件机制_c#_08              public virtual void FireEvent(MyEventArgs e)       //触发事件的方法

15C# 事件机制_.net_11              {

16C# 事件机制_c#_08                     if (OnSalaryCompute != null)

17C# 事件机制_.net_11                     {

18C# 事件机制_c#_08                            OnSalaryCompute(this,e);      //触发事件

19C# 事件机制_代理类_19                     }

20C# 事件机制_代理类_19              }

21C# 事件机制_代理类_19       }

22C# 事件机制_c#_08

23C# 事件机制_c#_08       public class MyEventArgs : EventArgs         //定义事件参数类

24C# 事件机制_.net_11       {

25C# 事件机制_c#_08              public readonly double _salary;

26C# 事件机制_c#_08              public MyEventArgs(double salary)

27C# 事件机制_.net_11              {

28C# 事件机制_c#_08                     this._salary = salary;

29C# 事件机制_代理类_19              }

30C# 事件机制_代理类_19       }

31C# 事件机制_c#_08

32C# 事件机制_c#_08       public class HumanResource

33C# 事件机制_.net_11       {

34C# 事件机制_c#_08              public void SalaryHandler(object sender,MyEventArgs e)          //事件处理函数,其签名应与代理签名相同

35C# 事件机制_.net_11              {

36C# 事件机制_c#_08                     Console.WriteLine("Salary is {0}",e._salary);     //只是打印一行字而已

37C# 事件机制_代理类_19              }

38C# 事件机制_c#_08

39C# 事件机制_c#_08              public static void Main()

40C# 事件机制_.net_11              {

41C# 事件机制_c#_08                     Employee ep = new Employee();

42C# 事件机制_c#_08                     HumanResource hr = new HumanResource();

43C# 事件机制_c#_08                     MyEventArgs e = new MyEventArgs(123.40);

44C# 事件机制_c#_08                     ep.OnSalaryCompute+=new SalaryCompute(hr.SalaryHandler);       //注册

45C# 事件机制_c#_08                     for (; ; )

46C# 事件机制_.net_11                     {

47C# 事件机制_c#_08                            Thread.Sleep(1000);      //让程序“睡”一秒

48C# 事件机制_c#_08                            ep.FireEvent(e);        //触发事件

49C# 事件机制_代理类_19                     }

50C# 事件机制_c#_08                     //Console.Read();

51C# 事件机制_代理类_19              }

52C# 事件机制_代理类_19       }

53C# 事件机制_代理类_41}

54C# 事件机制_事件处理_02


这个例子很有意思,它一秒钟自动触发事件一次,比上一个例子更能解释事件的机制,对吧?在这个例子中,我们要注意的一个地方就是事件处理函数的签名要和代理的签名一致