核心程式碼解析
// 1. 定義委派型別 (事件簽名)
public delegate void ActionEventHandler(object sender, EventArgs e);
// 2. 宣告事件 (基於委派)
public static ActionEventHandler Action;
// 3. 觸發事件的保護方法 (慣例命名 On + 事件名)
protected void OnAction(object sender, EventArgs e)
{
if (Action != null) // 防止無訂閱者時拋出 NullReferenceException
{
Action(sender, e);
}
}
// 4. 事件處理方法 (符合委派簽名)
public void MyHandler(object sender, EventArgs e)
{
// 處理邏輯...
}
// 5. 訂閱事件 (通常在 Form_Load 或建構函式中)
private void Form1_Load(object sender, EventArgs e)
{
Action += new ActionEventHandler(MyHandler); // 訂閱
OnAction(sender, e); // 觸發測試
}
關鍵概念圖解
| 元素 | 角色 | 關鍵字 |
| 委派 | 定義事件簽名 (參數/回傳型別) | delegate |
| 事件 | 發佈者暴露的通知點 | event |
| 訂閱 | 將處理方法加入事件調用清單 | += |
| 取消訂閱 | 從調用清單移除處理方法 | -= |
| 觸發 | 呼叫事件,依序執行所有訂閱者 | Invoke() |
標準事件模式 (Microsoft 設計指南)
public class MyClass
{
// 1. 事件引數類別 (攜帶資料)
public class MyEventArgs : EventArgs
{
public string Message { get; set; }
public int Code { get; set; }
}
// 2. 事件委派 (慣例: EventHandler 結尾)
public event EventHandler MyEvent;
// 3. 觸發方法 (慣例: On + 事件名, protected virtual)
protected virtual void OnMyEvent(MyEventArgs e)
{
MyEvent?.Invoke(this, e); // C# 6.0 null 條件運算子
}
// 4. 公開方法觸發事件
public void DoSomething()
{
OnMyEvent(new MyEventArgs { Message = "完成", Code = 200 });
}
}
// 使用端
var obj = new MyClass();
obj.MyEvent += (sender, e) => Console.WriteLine($"{e.Message}: {e.Code}");
obj.DoSomething();
進階主題
- 事件存取器:自訂
add/remove 邏輯 (如弱引用事件、執行緒安全)
- 介面事件:介面中宣告事件,實作類別需提供事件
- 靜態事件:全域通知機制,注意記憶體洩漏 (需明確取消訂閱)
- 非同步事件:使用
Task 或 BeginInvoke 非同步觸發
常見錯誤與最佳實務
| 錯誤 | 正確做法 |
| 直接呼叫事件 (Action()) | 透過 OnAction() 觸發,支援 null 檢查與衍生類別覆寫 |
| 忘記 null 檢查 | 使用 Action?.Invoke() (C# 6+) 或 if (Action != null) |
| 事件處理方法拋出例外中斷後續訂閱者 | 每個訂閱者包裝 try-catch,或使用 GetInvocationList() 手動逐個調用 |
| 未取消訂閱導致記憶體洩漏 | 在 Dispose/Unload 時執行 Action -= Handler |