首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 觀察者模式完整實作:事件與委派的經典應用

     編輯:關於C#

模式核心角色

  • Subject (主題):擁有狀態,狀態變更時通知所有觀察者。
  • Observer (觀察者):訂閱主題事件,收到通知時執行對應邏輯。
  • EventArgs:封裝通知所需的資料內容。

C# 語言級支援:event + delegate

.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 存取器。

實務應用場景

  • UI 控制項事件 (Button.Click, TextBox.TextChanged)
  • 領域事件 (Domain Events):訂單建立 → 發送通知、扣庫存、寫入日誌
  • 檔案系統監控:FileSystemWatcher 事件
  • 即時資料推播:SignalR Hub 方法對應觀察者

總結

C# 透過 delegate + event 內建觀察者模式,開發者只需定義 EventArgs、宣告 event、撰寫觸發邏輯與訂閱處理器,即可輕鬆實現鬆耦合的事件驅動架構。掌握進階主題(弱事件、非同步、執行緒安全)能進一步構建健壯的企業級應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved