C# 事件基於**委派** 實作,遵循**發布-訂閱** 模式。自訂事件需完成六個步驟:
using System;
namespace CustomEventDemo
{
// 1. 宣告委派:定義事件簽名 (sender, EventArgs)
public delegate void BarkEventHandler(object sender, BarkEventArgs e);
// 自訂事件參數:攜帶吠叫時間、聲音大小
public class BarkEventArgs : EventArgs
{
public DateTime Time { get; }
public int Volume { get; } // 分貝
public BarkEventArgs(DateTime time, int volume) => (Time, Volume) = (time, volume);
}
// 發布者:狗
public class Dog
{
// 2. 宣告事件
public event BarkEventHandler Bark;
// 3. 觸發事件的受保護虛擬方法 (慣例命名 On+事件名)
protected virtual void OnBark(BarkEventArgs e)
{
// 線程安全觸發:複製委派再檢查 null
BarkEventHandler handler = Bark;
handler?.Invoke(this, e);
}
// 業務邏輯:模擬狗吠
public void SimulateBark(int volume)
{
Console.WriteLine($"[Dog] 汪汪! 音量: {volume}dB");
OnBark(new BarkEventArgs(DateTime.Now, volume));
}
}
// 訂閱者:主人
public class Host
{
public Host(Dog dog)
{
// 5. 訂閱事件
dog.Bark += Dog_Bark;
}
// 4. 事件處理器
private void Dog_Bark(object sender, BarkEventArgs e)
{
Console.WriteLine($"[Host] 聽到狗吠! 時間: {e.Time:HH:mm:ss}, 音量: {e.Volume}dB → 起床查看");
}
}
class Program
{
static void Main()
{
// 6. 測試驗證
Dog dog = new Dog();
Host host = new Host(dog);
dog.SimulateBark(50);
dog.SimulateBark(80);
Console.ReadKey();
}
}
}
XXXEventHandler。(object sender, XXXEventArgs e),sender 為事件來源,e 攜帶資料。EventHandler (無泛型) 或 EventHandler<TEventArgs> (泛型,.NET 2.0+)。// 編譯器自動產生私有委派欄位 + add/remove 存取器
public event BarkEventHandler Bark;
// 等同於 (手動實作)
private BarkEventHandler _bark;
public event BarkEventHandler Bark
{
add { _bark += value; }
remove { _bark -= value; }
}
優勢:外部只能 `+=`/`-=`,**不能直接賦值 `=` 或呼叫 `Invoke`**,保護封裝性。
protected virtual void OnBark(BarkEventArgs e)
{
// 1. 取得副本避免競爭條件 (多執行緒同時 -=)
BarkEventHandler handler = Bark;
// 2. Null 條件運算子 ?. 呼叫 Invoke
handler?.Invoke(this, e);
}
.NET 4.5+ 也可用 Volatile.Read(ref _bark)?.Invoke(this, e); 更嚴格保證記憶體可見性。
System.EventArgs。EventArgs 結尾。訂閱者生命週期較長時,**必須在 Dispose/Unload 時 `-=` 取消訂閱**,否則發布者持有委派參考導致訂閱者無法被 GC 回收。
public void Unsubscribe(Dog dog) => dog.Bark -= Dog_Bark;
// 無需自訂委派型別
public event EventHandler Bark;
// 觸發
EventHandler handler = Bark;
handler?.Invoke(this, new BarkEventArgs(DateTime.Now, 60));
| 步驟 | 關鍵字/語法 | 說明 |
|---|---|---|
| 1. 委派 | delegate / EventHandler<T> | 定義簽名 |
| 2. 事件 | event | 封裝委派,僅允許 +/- |
| 3. 觸發 | OnXxx / handler?.Invoke | 線程安全、null 檢查 |
| 4. 處理器 | void Handler(sender, e) | 符合委派簽名 |
| 5. 訂閱 | obj.Event += Handler | 可多播 |
| 6. 觸發測試 | 呼叫業務方法 | 驗證流程 |