觀察者模式定義物件間一對多依賴:主題(被觀察者)狀態變更時,自動通知所有觀察者。核心角色:
public interface IObserver {
void Notify(object payload);
}
public interface IObservable {
void Register(IObserver observer);
void UnRegister(IObserver observer);
}
// 基礎主題實現
public class ObservableBase : IObservable {
private readonly Hashtable _observers = new Hashtable(); // 鍵值同參考,利用鍵唯一性
public void Register(IObserver o) => _observers.Add(o, o);
public void UnRegister(IObserver o) => _observers.Remove(o);
protected void NotifyObservers(object payload) {
foreach (IObserver o in _observers.Keys)
o.Notify(payload);
}
}
// 被觀察者:股票資料
public class StockData : ObservableBase {
private decimal _price;
public decimal Price {
get => _price;
set {
_price = value;
NotifyObservers(_price); // 狀態變更即時通知
}
}
}
// 觀察者:UI 顯示
public class StockDisplay : IObserver {
public void Notify(object payload) {
Console.WriteLine($"價格更新:{payload:C}");
}
}
// 使用
var data = new StockData();
var ui = new StockDisplay();
data.Register(ui);
data.Price = 100.5m; // UI 自動收到通知
data.UnRegister(ui); // 停止觀察
C# event + delegate 是觀察者模式的語言級內建實現,省去手工管理集合:
public class StockData {
// 宣告事件
public event EventHandler PriceChanged;
private decimal _price;
public decimal Price {
get => _price;
set {
if (_price != value) {
_price = value;
OnPriceChanged(new PriceChangedEventArgs(value));
}
}
}
protected virtual void OnPriceChanged(PriceChangedEventArgs e) {
PriceChanged?.Invoke(this, e); // 安全引發
}
}
public class PriceChangedEventArgs : EventArgs {
public decimal NewPrice { get; }
public PriceChangedEventArgs(decimal price) => NewPrice = price;
}
// 訂閱端
var data = new StockData();
data.PriceChanged += (s, e) => Console.WriteLine($"新價格:{e.NewPrice:C}");
data.Price = 200.0m;
| 維度 | 手工介面實現 | .NET 事件/委派 |
|---|---|---|
| 樣板代碼 | 多(集合管理、線程安全) | 極少 |
| 型別安全 | 傳 object 需轉型 | 強型別 EventArgs |
| 多播支援 | 手動迴圈 | 內建 +=/-= |
| 語法糖 | 無 | Lambda、匿名方法、事件存取器 |
| 適用場景 | 跨語言、非 .NET 環境、極度底層控制 | 絕大多數 .NET 業務開發 |
WeakEventManager)。Task.Run(() => observer.Notify(payload)) 或 INotifyPropertyChanged 數據繫結。IObservable 將事件流轉為可組合、可查詢的資料流。觀察者模式是解耦的基石。.NET 開發者應優先使用 event/delegate,僅在特殊需求下手工實作。理解其內部機制,有助於診斷記憶體洩漏、死鎖與效能問題。