==============
@ 在類中定義事件
==============
using System;
// Mail 管理。
class MailManager{
// 1.定義事件參數,用於向事件接受者傳遞附加資訊。
// 事件參數類都應該繼承自 System.EventArgs,並且類型名稱以 EventArgs 結束。
public class MailMsgEventArgs : EventArgs{
public MailMsgEventArgs(string from, string to, string subject, string body) {
this.from = from; // 寄件者。
this.to = to; // 接受者。
this.subject = subject; // 主題。
this.body = body; // 本文。
}
public readonly string from, to, subject, body;
}
// 2.定義一個委託類型,用於指定事件觸發時被調用的方法原型。
// 委託類型的名稱應該以 EventHandler 結束。另外方法原型傳回值應該為 void,並且有兩個參數:
// 第一個參數指向發送通知的對象,第二個是事件參數。如果我們定義的時間沒有需要傳遞給事件接受者
// 的附加資訊,就不需要定義新的委託類型,直接使用 System.EventHandler,並將 EventArgs.Empty
// 傳遞給第二個參數即可。
public delegate void MailMsgEventHandler(Object sender, MailMsgEventArgs args);
// 3.定義事件。
// 如下定義的事件的名稱是 MailMsg,類型為 MailMsgEventHandler,其含義為所有事件接受者都必須提供
// 一個原型和 MailMsgEventHandler 想匹配的方法。
public event MailMsgEventHandler MailMsg;
// 4. 觸發事件。將一些外部輸入轉化為觸發事件的動作。
public void SimulateArrivingMsg(string from, string to, string subject, string body) {
MailMsgEventArgs e = new MailMsgEventArgs(from, to, subject, body);
// 調用虛方法通知對象事件已發生,如果衍生類別型沒有重寫該虛方法,
// 對象將通知所有登記的時間偵聽者。
OnMailMsg(e);
}
// 5.事件處理,負責通知事件的偵聽者。
protected virtual void OnMailMsg(MailMsgEventArgs e) {
if (MailMsg != null) {
// 將事件通知給委託鏈表上的所有對象。
MailMsg(this, e);
}
}
}
======================
@ 編譯器對事件定義語句的處理
======================
對於上面代碼中的定義事件的語句:
public event MailMsgEventHandler MailMsg;
編譯器會將其翻譯為如下三個方法:
// 1. 一個初始化為 null 的"私人"委託類型欄位。
private MailMsgEventHandler MailMsg = null;
// 2. 一個允許其他對象登記事件的"公有" add_* 方法。
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void add_MailMsg(MailMsgEventHandler handler){
this.MailMsg = (MailMsgEventHandler)Delegate.Combine(MailMsg, handler);
}
// 3. 一個允許其他對象登出事件的"公有" remove_* 方法。
[MethodImplAttribute(MethodImplOptions.Sysnchronized)]
public void remove_MailMsg(MailMsgEventHandler handler){
MailMsg = (MailMsgEventHandler).Delegate.Remove(MailMsg, handler);
}
add_* 和 remove_* 方法都應用了一個 MethodImplAttribute 特性,這些方法被標識為同步方法,使得它們得以
實現安全執行緒:也就是多個事件偵聽者可以同時登記或者登出事件,而不損壞委託鏈表。這兩個方法的訪問限制
取決於事件的訪問限制,如果事件被定義為受保護事件,那麼這兩個方法也將為受保護方法。
============
@ 定義偵聽事件的類
============
// 傳真機偵聽 MailManager 的 MailMsg 事件,並發送傳真(輸出到控制台)。
class Fax
{
// 將 MailManager 對象傳遞給建構函式。
public Fax(MailManager mm) {
// 構造一個指向 FaxMsg 回調方法的 MailMsgEventHandler 委託執行個體,
// 然後登記 MailManager 的 MailMsg 事件。
mm.MailMsg += new MailManager.MailMsgEventHandler(FaxMsg);
}
// MailManager 將調用該方法來通知 Fax 對象收到到一個新的電子郵件訊息。
// sender: 表示 MailManager 對象,如果希望和事件的觸發者通訊,可以使用該參數。
// e:表示 MailManager 對象提供的一些附加事件資訊。
private void FaxMsg(Object sender, MailManager.MailMsgEventArgs e) {
Console.WriteLine(" From: {0} \n To: {1} \n Subject: {2} \n Body: {3} \n" +
e.from, e.to, e.subject, e.body);
}
// 登出事件。
public void Unregister(MailManager mm) {
// 建立一個新的委託執行個體是為了將其從委託鏈表中移除(參見《深入理解委託》)
MailManager.MailMsgEventHandler callback = new MailManager.MailMsgEventHandler(FaxMsg);
mm.MailMsg -= callback;
}
}
========
@ 使用事件
========
using System;
public class Test
{
public static void Main(string[] args) {
// 定義事件寄件者 mm。
MailManager mm = new MailManager();
// 定義事件偵聽者,偵聽 mm 發送的時間。
Fax fax = new Fax(mm);
// 觸發事件寄件者定義的事件。
mm.SimulateArrivingMsg("Me", "You", "Love", "I love you !");
}
}
輸出:
From: Me
To: You
Subject: Love
Body: I love you !