12.1 發佈者和訂閱者
很多程序都有一個共同的需求,即當一個特定的程序事件發生時,程序的其他部分可以得到該事件已經發生的通知
發佈者/訂閱者模式(publisher/subscriber pattern)可以滿足這種需求。在這種模式中,發佈者類定義了一系列程序的其他部分可能感興趣的事件。其他類可以“註冊”,以便在這些事件發生時發佈者可以通知它們。這些訂閱者類通過向發佈者提供一個方法來“註冊”以獲取通知。當事件發生時,發佈者“觸發事件”,然後執行訂閱者提交的所有事件
由訂閱者提供的方法稱爲回調方法,因爲發佈者通過執行這些方法來“往回調用訂閱者的方法”。還可以將它們稱爲事件處理程序,因爲它們是爲處理事件而調用的代碼
ps:
注意
- 發佈者(publisher) 發佈某個事件的類或結構,其他類可以在該事件發生時得到通知
- 訂閱者(subscriber) 註冊並在事件發生時得到通知的類或結構。
- 事件處理程序(event handler) 由訂閱者註冊到事件的方法,在發佈者觸發事件時執行。事件處理程序方法可以定義在事件所在的類或結構中,也可以定義在不同的類或結構中
- 觸發(raise)事件 調用(invoke)或觸發(fire)事件的術語。當事件觸發時,所有註冊到它的方法都會被依次調用
12.2 源代碼組件概覽
需要在事件中使用的代碼有5部分
- 委託類型聲明 事件和事件處理程序必須有共同的簽名和返回類型,它們通過委託類型進行描述
- 事件處理程序聲明 訂閱者類中會在事件觸發時執行的方法聲明。它們不一定是有顯式命名的方法,還可以是匿名方法或Lambda表達式
- 事件聲明 發佈者類必須聲明一個訂閱者類可以註冊的事件成員。當聲明的事件爲public時,稱爲發佈了事件
- 事件註冊 訂閱者必須訂閱事件才能在它被觸發時得到通知
- 觸發事件的代碼 發佈者類中“觸發”事件並導致調用註冊的所有事件處理程序的代碼
ps:
12.3 聲明事件
發佈者類必須提供事件對象。創建事件比較簡單——只需要委託類型和名字。事件聲明的語法如下代碼所示,代碼中聲明瞭一個叫做CountADozen的事件。注意如下有關CountedADozen事件的內容
- 事件聲明在一個類中
- 它需要委託類型的名稱,任何附加到事件(如註冊)的處理程序都必須與委託類型的簽名和返回類型匹配
- 它聲明爲public,這樣其他類和結構可以在它上面註冊事件處理程序
- 不能使用對象創建表達式(new表達式)來創建它的對象
class Incrementer
{
//實例事件
// 關鍵字 委託類型 事件名
public event EventHandler CountedADozen;
//或
public event EventHandler MyEvent1,MyEvent2,MyEvent3;
//靜態事件
public static event EventHandler CountedADozen;
}
事件是成員:
事件和方法、屬性一樣。是類或結構的成員
重要特性:
- 由於事件是成員:
- 我們不能在一段可執行代碼中聲明事件
- 它必須聲明在類或結構中,和其他成員一樣
- 事件成員被隱式自動初始化爲null
事件聲明需要委託類型的名字,我們可以聲明一個委託類型或使用已存在的。如果我們聲明一個委託類型,它必須指定事件保存的方法的簽名和返回類型
12.4 訂閱事件
訂閱者向事件添加事件處理程序。對於一個要添加到事件的事件處理程序來說,它必須具有與事件的委託相同的返回類型和簽名
- 使用
+=
運算符來爲事件增加事件處理程序,如下面代碼所示。事件處理程序位於該運算符的右邊。 - 事件處理程序的規範可以是以下任意一種:
- 實例方法的名稱
- 靜態方法的名稱
- 匿名方法
- Lambda表達式
eg: 爲 CountedADozen
事件添加方法
// 類 事件成員 實例方法
incrementer.CountedADozen += IncrementOozensCount; //方法引用形式
// 靜態方法
incrementer.CountedADozen += ClassB.CountexHandlerB; //方法引用形式
// 委託形式
mc.CountedADozen += new EventHandler(cc.CounterHandlerC);
// Lambda表達式
incrementer.CountedADozen +=()=> DozensCount++;
// 匿名方法
incrementer.CountedADozen += delegate(DozensCount++;};
12.5 觸發事件
事件成員本身只是保存了需要被調用的事件處理程序。如果事件沒有被觸發,什麼都不會發生。我們需要確保在合適的時候有代碼來做這件事情
例如,如下代碼觸發了 CountedADozen
事件。注意如下有關代碼的事項
- 在觸發事件之前和
null
進行比較,從而查看是否包含事件處理程序,如果事件是null,則表示沒有,不能執行 - 觸發事件的語法和調用方法一樣:
- 使用事件名稱,後面跟的參數列表包含在圓括號中
- 參數列表必須與事件的委託類型相匹配
if (CountedADozen != null) // 確認有方法可以執行
{
CountedADozen(source,args); // 觸發事件
}
移除事件處理程序
在用完了事件處理程序之後,可以從事件中把它移除。可以利用-=運算符把事件處理程序從事件中移除,如下所示:
p.SimpleEvent -= s.MethodB; //移除事件處理程序HethodB
12.6 事件訪問器
- 有兩個訪問器:add和remove
- 聲明事件的訪問器看上去和聲明一個屬性差不多
public event EventHandler CountedADozen
{
add
{
... //執行+=運算符的代碼
}
remove
{
... //執行-=運算符的代碼
}
}
聲明瞭事件訪問器之後,事件不包含任何內嵌委託對象。我們必須實現自己的機制來存儲和移除事件註冊的方法
事件訪問器表現爲 void
方法,也就是不能使用包含返回值的 return
語句