.NET 內建 event 關鍵字與委派模式,直接對應觀察者模式,無需手造介面與清單管理。
using System;
using System.Windows.Forms; // 需參考 System.Windows.Forms.dll
namespace ObserverPattern
{
// 1. 事件資料封裝
public class NumberEventArgs : EventArgs
{
public int Number { get; }
public NumberEventArgs(int number) => Number = number;
}
// 2. 主題:監聽迴圈,觸發事件
public class Subject
{
// 定義事件 (委派簽名)
public event EventHandler<NumberEventArgs> NumberReached;
public void RunLoop(int target)
{
for (int i = 0; i <= 100; i++)
{
if (i == target)
{
OnNumberReached(new NumberEventArgs(i));
break; // 示範只觸發一次
}
}
}
// 受保護虛擬方法:允許衍生類別自訂觸發邏輯
protected virtual void OnNumberReached(NumberEventArgs e)
{
NumberReached?.Invoke(this, e); // Null 條件運算子,簡化 null 檢查
}
}
// 3. 觀察者:靜態方法與實例方法皆可訂閱
public class Observers
{
public static void MessageBoxHandler(object sender, NumberEventArgs e)
{
MessageBox.Show($"靜態觀察者收到:{e.Number}", "通知");
}
public void ConsoleHandler(object sender, NumberEventArgs e)
{
Console.WriteLine($"實例觀察者收到:{e.Number}");
}
}
// 4. 用戶端程式
class Program
{
[STAThread] // MessageBox 需 STA 執行緒
static void Main()
{
Console.Write("請輸入 0-100 目標數字:");
if (!int.TryParse(Console.ReadLine(), out int target) || target < 0 || target > 100)
{
Console.WriteLine("輸入錯誤");
return;
}
var subject = new Subject();
var obs = new Observers();
// 訂閱事件 ( += )
subject.NumberReached += Observers.MessageBoxHandler;
subject.NumberReached += obs.ConsoleHandler;
subject.RunLoop(target);
// 取消訂閱 (-=) 示範
subject.NumberReached -= obs.ConsoleHandler;
Console.WriteLine("已移除 Console 觀察者,再跑一次:");
subject.RunLoop(target);
Console.ReadKey();
}
}
}
| 主題 | 說明 |
|---|---|
| 弱事件模式 | 避免觀察者未取消訂閱導致記憶體洩漏,可用 WeakEventManager (WPF) 或自訂弱委派。 |
| 執行緒安全 | 事件觸發時若觀察者在不同執行緒,需自行 `SynchronizationContext.Post` 或鎖定。 |
| 非同步通知 | 改用 `event Func<TEventArgs, Task>` 搭配 `await Task.WhenAll` 並行通知。 |
| 訂閱過濾 | 委派簽名加入 `Predicate<T>` 讓觀察者自行決定是否處理。 |
// 傳統 GoF 介面寫法 (教學用,實務較少見)
public interface IObserver { void Update(int number); }
public interface ISubject { void Attach(IObserver o); void Detach(IObserver o); void Notify(); }
public class ConcreteSubject : ISubject
{
private List<IObserver> _observers = new();
public int State { get; set; }
public void Attach(IObserver o) => _observers.Add(o);
public void Detach(IObserver o) => _observers.Remove(o);
public void Notify() => _observers.ForEach(o => o.Update(State));
}
.NET event 模式優點:型別安全、語法簡潔、支援多播、IDE 智慧提示、自動產生 add/remove 存取器。
FileSystemWatcher 事件C# 透過 delegate + event 內建觀察者模式,開發者只需定義 EventArgs、宣告 event、撰寫觸發邏輯與訂閱處理器,即可輕鬆實現鬆耦合的事件驅動架構。掌握進階主題(弱事件、非同步、執行緒安全)能進一步構建健壯的企業級應用。