C# 事件機制

隨夢而飛發表於2015-04-13

轉自:http://www.cnblogs.com/donghaiyiyu/archive/2007/07/29/828738.html

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

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

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

用物件導向的語言解釋,這兩者的意義如下:

事件發行者(Publisher)

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

事件接收者(Receiver)

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

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

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

 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4
 5namespace EventDemo
 6{
 7       public delegate void SalaryCompute();        //宣告一個代理類
 8
 9       public class Employee
10       {
11              public event SalaryCompute OnSalaryCompute;         //定義事件,將其與代理繫結
12
13              public virtual void FireEvent()       //觸發事件的方法
14              {
15                     if (OnSalaryCompute != null)
16                     {
17                            OnSalaryCompute();      //觸發事件
18                     }

19              }

20       }

21
22       public class HumanResource
23       {
24              public void SalaryHandler()          //事件處理函式
25              {
26                     Console.WriteLine("Salary");     //只是列印一行字而已
27              }

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

37       }

38}

39
在這個例子中,Employee類相當於一個事件釋出者(Publisher),它定義了事件的相關資訊,包括定義了一個事件用於計算薪水(OnSalaryCompute),以及一個觸發事件的函式(FireEvent()),為簡單起見,本例沒有加上事件引數。

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

下面將這個例子改造一下,事件引數資訊,用以完善事件機制。

 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Threading;
 5
 6namespace EventDemo
 7{
 8       public delegate void SalaryCompute(object sender,MyEventArgs e);        //宣告一個代理類
 9
10       public class Employee
11       {
12              public event SalaryCompute OnSalaryCompute;         //定義事件,將其與代理繫結
13
14              public virtual void FireEvent(MyEventArgs e)       //觸發事件的方法
15              {
16                     if (OnSalaryCompute != null)
17                     {
18                            OnSalaryCompute(this,e);      //觸發事件
19                     }

20              }

21       }

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

30       }

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

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

50                     //Console.Read();
51              }

52       }

53}

54

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


事件與委託的區別:

(摘自:http://zhidao.baidu.com/link?url=z3JDCDjTClL3yzNb1YzOeIZUjQ-soKSKLBj0-spT2TxfybKnM4tQxdIbiwKBowfGDtg52wgYOS-gXFzz1yx-aK

事件就是一個狹義的委託,也就是事件是一個用於事件驅動模型的專用委託.

通俗的講,委託你可以在客戶程式碼中直接呼叫委託來激發委託指向的函式,而事件不可以,事件的觸發只能由服務程式碼自己觸發

也就是說在你的程式碼裡委託你不但可以安排誰是它的呼叫函式,還可以直接呼叫它,而事件不能直接呼叫,只能通過某些操作觸發

你可以理解事件就是一個或多個委託,此話應該有誤的吧,事件可以有多個事件處理函式,委託同樣也可以是個多播委託

相關文章