1. 實作一個 System.EventArgs 的子代, 強烈建議設計成 immutable. C# 建議名字最好叫做 XxxEventArgs, 例如:
public class NewMailEventArgs : EventArgs { ...; }
2. 於發送端 class 定義一個 event member 如下 (不一定要 public), 根據命名規範, 應該要大寫開頭
public class MailManager {
public event EventHandler<NewMailEventArgs> NewMail;
}
3. 於發送端 class 定義一個 protected virtual method 如下:
public class MailManager {
protected virtual void OnNewMail(NewMailEventArgs e) {
EventHandler<NewMailEventArgs> temp = this.NewMail;
if(temp != null) temp(this, e);
}
...
}
4. 當事件發生時, 呼叫上述的 protected virtual method
-------------------------------------------------------------------------
重點:
步驟 3 之所以要切成兩半, 是為了防止多緒時的 race condition. 因為 delegate 是 immutable, 故只需如步驟 3 所做就可以達成防止 race condition 的需求.
不過步驟 3 有一個問題: 若 compiler 夠聰明, 它可能會將步驟 3 優化如下:
protected virtual void OnNewMail(NewMailEventArgs e) {
if(this.newMail != null) this.NewMail(this, e);
}
CLR 為了保證回溯相容性, 故不會做這種優化. 但為了防止未來版本的 CLR 做此優化, 寫 .NET 4.0 以上的 C# 程式時可以把步驟 3 改成這樣:
public class MailManager {
protected virtual void OnNewMail(NewMailEventArgs e) {
EventHandler<NewMailEventArgs> temp = Volatile.Read(ref this.NewMail);
if(temp != null) temp(this, e);
}
...
}
-------------------------------------------------------------------------
步驟三其實是可以被公式化的, 以下是透過 class extension 來對步驟 3 做公式化的方法:
public static class EventArgExtensions {
public static void Raise<T>(this T e, Object sender, ref EventHandler<T> eventDelegate) {
EventHandler<T> temp = Volatile.Read(ref eventDelegate);
if(temp != null) temp(sender, e);
}
}
於是原來的步驟三可以寫成這樣:
protected virtual void OnNewMail(NewMailEventArgs e) {
e.Raise(this, ref this.NewMail);
}
-------------------------------------------------------------------------
EventHandler 其實是一個 delegate, 宣告如下:
public delegate void EventHandler<T>(Object sender, T e);
-------------------------------------------------------------------------
C# Compiler 其實會把下面這宣告
public class MailManager {
public event EventHandler<NewMailEventArgs> NewMail;
}
偷偷的改寫為這樣:
public class MailManager {
private EventHandler<NewMailEventArgs> NewMail = null;
public void add_NewMail(EventHandler<NewMailEventArgs> value) {
...; // 將 value 以 thread-safe 的方式加入 this.NewMail
}
public voie remove_NewMail(EventHandler<NewMailEventArgs> value){
...; // 將 value 以 thread-safe 的方式自 this.NewMail 中移除
}
}
注意!!! add_XXX 以及 remove_XXX 的宣告修飾詞會與當初 event 的修飾詞一樣. 如果當初的 event 是 virtual 則 compiler 產生出來的這兩個 methods 就會是 virtual. 其他如 protected, private static 亦然.
-------------------------------------------------------------------------
以下是一個事件接收端的案例:
public class Fax {
public Fax(MailManager mm) {
mm.NewMail += this.FaxMsg;
}
public void Unregister(MailManager mm) {
mm.NewMail -= this.FaxMsg;
}
private void FoxMsg(Object sender, NewMailEventArgs e) {
...; // 處理之
}
}
上面的 mm.NewMail += this.FaxMsg; 其實會被 compiler 改為:
mm.add_NewMail(new EventHandler<NewMailEventArgs>(this.FaxMsg));
mm.NewMail -= this.FaxMsg; 亦會被 compiler 改為:
mm.remove_NewMail(new EventHandler<NewMailEventArgs>(FaxMsg));
沒有留言:
張貼留言