C#事件機制

在所有關於C#事件機制的介紹中,我更傾向於發佈者/訂閱者(Publisher/Subscriber)這種描述。理解事件機制並不是一件容易的事情,它所涉及的思想值得我們好好去研究。

本文資源來自《C#與.NET技術平臺實戰演練》——中國青年出版社

談到事件,我們涉及到兩個角色:事件發佈者(Publisher)和事件訂閱者(Scriber),也可以說是事件發送者(Sender)和事件接收者(Receiver)的關係。舉個例子來說,市面上目前有許多雜誌,雜誌的種類也很多。而我只對其中的某些感興趣,那麼我就可以向雜誌發行商提出訂閱。之後,每當雜誌發行時,我就會收到我在雜誌發行商那兒訂閱的雜誌。在這個關係中,雜誌發行商就相當於事件發行者,而我就是事件訂閱者。每當雜誌發行時,就觸發了一個發行事件。

用面向對象的語言解釋,這兩者的意義如下:

事件發行者(Publisher)

它是一個對象,且會維護自身的狀態信息。每當狀態信息發生變動時,便觸發一個事件,並通知所有的事件訂閱者。對於雜誌發行商來說,每本雜誌都有自己的信息在裏面,當雜誌發行時,我要通知訂閱該雜誌的人:雜誌已經發行啦,請注意查收!

事件接收者(Receiver)

這個對象要註冊它感興趣的對象,也就是訂閱它自己喜歡的雜誌啦。另外,這個對象通常要提供一個事件處理方法,在事件發行者觸發一個事件後,會自動執行這個方法。對於上面所舉的例子來說,也就是我收到雜誌後要做什麼事情,比如,你可以滿世界地大喊:我收到雜誌啦!也可以將雜誌收藏起來慢慢欣賞,具體怎麼實現完全取決你自己的喜好。

以下是.NET事件處理機制的模型:



 下面給一個簡單的例子,用以闡述事件的思想:

 1None.gifusing System;
 2None.gifusing System.Collections.Generic;
 3None.gifusing System.Text;
 4None.gif
 5None.gifnamespace EventDemo
 6ExpandedBlockStart.gifContractedBlock.gifdot.gif{
 7InBlock.gif       public delegate void SalaryCompute();        //聲明一個代理類
 8InBlock.gif
 9InBlock.gif       public class Employee
10ExpandedSubBlockStart.gifContractedSubBlock.gif       dot.gif{
11InBlock.gif              public event SalaryCompute OnSalaryCompute;         //定義事件,將其與代理綁定
12InBlock.gif
13InBlock.gif              public virtual void FireEvent()       //觸發事件的方法
14ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
15InBlock.gif                     if (OnSalaryCompute != null)
16ExpandedSubBlockStart.gifContractedSubBlock.gif                     dot.gif{
17InBlock.gif                            OnSalaryCompute();      //觸發事件
18ExpandedSubBlockEnd.gif                     }

19ExpandedSubBlockEnd.gif              }

20ExpandedSubBlockEnd.gif       }

21InBlock.gif
22InBlock.gif       public class HumanResource
23ExpandedSubBlockStart.gifContractedSubBlock.gif       dot.gif{
24InBlock.gif              public void SalaryHandler()          //事件處理函數
25ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
26InBlock.gif                     Console.WriteLine("Salarydot.gifdot.gif");     //只是打印一行字而已
27ExpandedSubBlockEnd.gif              }

28InBlock.gif
29InBlock.gif              public static void Main()
30ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
31InBlock.gif                     Employee ep = new Employee();
32InBlock.gif                     HumanResource hr = new HumanResource();
33InBlock.gif                     ep.OnSalaryCompute+=new SalaryCompute(hr.SalaryHandler);       //註冊
34InBlock.gif                     ep.FireEvent();        //觸發事件
35InBlock.gif                     Console.Read();
36ExpandedSubBlockEnd.gif              }

37ExpandedSubBlockEnd.gif       }

38ExpandedBlockEnd.gif}

39None.gif在這個例子中,Employee類相當於一個事件發佈者(Publisher),它定義了事件的相關信息,包括定義了一個事件用於計算薪水(OnSalaryCompute),以及一個觸發事件的函數(FireEvent()),爲簡單起見,本例沒有加上事件參數。

與之相對應,HumanResource類則相當於一個事件訂閱者(Subscriber),它定義了一個事件處理函數(SalaryHandler()),並用+=將其與事件聯繫起來,從而使事件觸發的時候能夠調用我這個方法(在本例中也就是打印一行字啦)。值得注意的一點是,事件處理函數的方法簽名要與代理的方法簽名相同,這是非常重要的一點。

下面將這個例子改造一下,事件參數信息,用以完善事件機制。

 1None.gifusing System;
 2None.gifusing System.Collections.Generic;
 3None.gifusing System.Text;
 4None.gifusing System.Threading;
 5None.gif
 6None.gifnamespace EventDemo
 7ExpandedBlockStart.gifContractedBlock.gifdot.gif{
 8InBlock.gif       public delegate void SalaryCompute(object sender,MyEventArgs e);        //聲明一個代理類
 9InBlock.gif
10InBlock.gif       public class Employee
11ExpandedSubBlockStart.gifContractedSubBlock.gif       dot.gif{
12InBlock.gif              public event SalaryCompute OnSalaryCompute;         //定義事件,將其與代理綁定
13InBlock.gif
14InBlock.gif              public virtual void FireEvent(MyEventArgs e)       //觸發事件的方法
15ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
16InBlock.gif                     if (OnSalaryCompute != null)
17ExpandedSubBlockStart.gifContractedSubBlock.gif                     dot.gif{
18InBlock.gif                            OnSalaryCompute(this,e);      //觸發事件
19ExpandedSubBlockEnd.gif                     }

20ExpandedSubBlockEnd.gif              }

21ExpandedSubBlockEnd.gif       }

22InBlock.gif
23InBlock.gif       public class MyEventArgs : EventArgs         //定義事件參數類
24ExpandedSubBlockStart.gifContractedSubBlock.gif       dot.gif{
25InBlock.gif              public readonly double _salary;
26InBlock.gif              public MyEventArgs(double salary)
27ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
28InBlock.gif                     this._salary = salary;
29ExpandedSubBlockEnd.gif              }

30ExpandedSubBlockEnd.gif       }

31InBlock.gif
32InBlock.gif       public class HumanResource
33ExpandedSubBlockStart.gifContractedSubBlock.gif       dot.gif{
34InBlock.gif              public void SalaryHandler(object sender,MyEventArgs e)          //事件處理函數,其簽名應與代理簽名相同
35ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
36InBlock.gif                     Console.WriteLine("Salary is {0}",e._salary);     //只是打印一行字而已
37ExpandedSubBlockEnd.gif              }

38InBlock.gif
39InBlock.gif              public static void Main()
40ExpandedSubBlockStart.gifContractedSubBlock.gif              dot.gif{
41InBlock.gif                     Employee ep = new Employee();
42InBlock.gif                     HumanResource hr = new HumanResource();
43InBlock.gif                     MyEventArgs e = new MyEventArgs(123.40);
44InBlock.gif                     ep.OnSalaryCompute+=new SalaryCompute(hr.SalaryHandler);       //註冊
45InBlock.gif                     for (; ; )
46ExpandedSubBlockStart.gifContractedSubBlock.gif                     dot.gif{
47InBlock.gif                            Thread.Sleep(1000);      //讓程序“睡”一秒
48InBlock.gif                            ep.FireEvent(e);        //觸發事件
49ExpandedSubBlockEnd.gif                     }

50InBlock.gif                     //Console.Read();
51ExpandedSubBlockEnd.gif              }

52ExpandedSubBlockEnd.gif       }

53ExpandedBlockEnd.gif}

54None.gif
這個例子很有意思,它一秒鐘自動觸發事件一次,比上一個例子更能解釋事件的機制,對吧?在這個例子中,我們要注意的一個地方就是事件處理函數的簽名要和代理的簽名一致



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章