觀察者模式定義物件間的一對多依賴,當主題狀態改變時,自動通知所有觀察者。C# 內建 delegate 與 event 關鍵字,使此模式實作極為自然。
| 觀察者術語 | C# 對應 |
|---|---|
| Subject(主題) | 擁有 event 的類別(如 CCollection) |
| Observer(觀察者) | 訂閱事件的類別(如 BusinessObject) |
| 通知 | 引發 event |
| 回呼 | 委派方法 |
using System;
// 事件參數:攜帶 Key/Value
class ItemEventArgs : EventArgs
{
public readonly string Key;
public readonly long Value;
public ItemEventArgs(string key, long value) { Key = key; Value = value; }
}
// 主題:集合類別
class ObservableCollection
{
// 1. 定義委派型別
public delegate void ItemAddedHandler(object sender, ItemEventArgs e);
// 2. 宣告事件
public event ItemAddedHandler ItemAdded;
// 3. 觸發事件的受保護方法
protected virtual void OnItemAdded(ItemEventArgs e)
{
ItemAdded?.Invoke(this, e); // C# 6.0+ null 條件運算子
}
// 業務方法
public void AddItem(string key, long value)
{
// ... 實際新增邏輯 ...
OnItemAdded(new ItemEventArgs(key, value));
}
}
// 觀察者:業務物件
class BusinessObject
{
private string _name;
public BusinessObject(ObservableCollection coll, string name)
{
_name = name;
// 訂閱事件(使用 += 運算子)
coll.ItemAdded += OnItemAdded;
}
// 事件處理器
private void OnItemAdded(object sender, ItemEventArgs e)
{
Console.WriteLine($"[{_name}] 收到新項目: Key={e.Key}, Value={e.Value}");
}
}
// 測試
class Program
{
static void Main()
{
var coll = new ObservableCollection();
var bo1 = new BusinessObject(coll, "BO-1");
var bo2 = new BusinessObject(coll, "BO-2");
coll.AddItem("UserCount", 100);
coll.AddItem("OrderID", 2024001);
}
}
[BO-1] 收到新項目: Key=UserCount, Value=100
[BO-2] 收到新項目: Key=UserCount, Value=100
[BO-1] 收到新項目: Key=OrderID, Value=2024001
[BO-2] 收到新項目: Key=OrderID, Value=2024001
coll.ItemAdded -= OnItemAdded; // 必須使用相同的委派實例
長生命週期的主題持有短生命週期觀察者的強參考會造成記憶體洩漏。可使用 WeakEventManager (WPF) 或自訂弱參考包裝器解決。
// 非同步引發(需自行管理執行緒)
Task.Run(() => OnItemAdded(e));
ItemAdded、OrderCompleted。(object sender, TEventArgs e) 慣例。?.Invoke 防止 null 參考。IDisposable 時在 Dispose 中取消訂閱。