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

C# 事件委派 vs Java 觀察者模式深度對比:語言內建 vs 設計模式的實現差異

     編輯:關於C語言

前言

C# event/delegate 機制與 Java 觀察者模式(Observer/Observable)同為「發布-訂閱」模式的實現,但前者為語言內建關鍵字,後者為設計模式+介面。本文透過完整代碼對比,深度解析兩者在結構、語法、執行緒安全、記憶體管理等面向的異同。

C# 事件實現完整代碼

// 1. 定義委派型別(相當於觀察者介面的 Update 方法簽名)
public delegate void ChangedEventHandler(object sender, EventArgs e);

// 2. 主題類別(Subject / Observable)
public class ListWithChangedEvent : ArrayList
{
    // 事件聲明(編譯器自動產生 add/remove/raise 方法)
    public event ChangedEventHandler Changed;

    // 觸發方法(受保護虛擬,允許衍生類別擴展)
    protected virtual void OnChanged(EventArgs e)
    {
        Changed?.Invoke(this, e); // Null-conditional 安全觸發
    }

    // 覆寫會改變集合的方法,操作後觸發事件
    public override int Add(object value)
    {
        int i = base.Add(value);
        OnChanged(EventArgs.Empty);
        return i;
    }
    public override void Clear() { base.Clear(); OnChanged(EventArgs.Empty); }
    public override object this[int index]
    {
        set { base[index] = value; OnChanged(EventArgs.Empty); }
    }
}

// 3. 觀察者類別(Observer / Listener)
public class EventListener
{
    private ListWithChangedEvent _list;

    public EventListener(ListWithChangedEvent list)
    {
        _list = list;
        // 訂閱(相當於 AddObserver)
        _list.Changed += ListChanged;
    }

    private void ListChanged(object sender, EventArgs e)
    {
        Console.WriteLine("Event fired: list changed");
        Thread.Sleep(5000); // 模擬耗時處理
    }

    public void Detach()
    {
        // 退訂(相當於 RemoveObserver)
        _list.Changed -= ListChanged;
        _list = null;
    }
}

// 4. 測試程式
public class Test
{
    public static void Main()
    {
        var list = new ListWithChangedEvent(); // 建立 Subject
        var listener = new EventListener(list); // 建立 Observer 並訂閱

        list.Add("item 1"); // 觸發事件
        list.Clear();       // 再次觸發

        listener.Detach();  // 移除觀察者
    }
}

Java 觀察者模式對等代碼(概念對照)

// Java 介面定義
interface Observer { void Update(Observable o, Object arg); }
interface Observable { void AddObserver(Observer o); void RemoveObserver(Observer o); void NotifyObservers(); }

// 主題實作
class ListWithChangedEvent extends Observable {
    public void Add(Object value) {
        super.add(value);
        setChanged();
        notifyObservers(); // 遍歷觀察者呼叫 Update
    }
}

// 觀察者實作
class EventListener implements Observer {
    public void Update(Observable o, Object arg) {
        System.out.println("List changed");
    }
}

核心差異深度對比

面向C# Event/DelegateJava Observer Pattern
語言支援關鍵字 event/delegate,編譯器產生訂閱/發佈邏輯純介面+手動實作,無語言層級支援
型別安全委派簽名強型別,編譯期檢查Object 參數需轉型,執行期風險
多播支援原生支援 +=/-= 多播委派需自行維護 List<Observer>
語法簡潔度obj.Event += Handler; 一行訂閱subject.addObserver(observer) 需實作介面類別
記憶體洩漏風險事件持有委派參考,若不退訂會阻礙 GC(需 -= 或弱事件模式)同樣需 removeObserver,否則 Observable 持有強參考
執行緒安全事件觸發非原子,需自行鎖定或用 Interlocked 模式同樣非原子,Observable 內部同步僅保護列表結構
延遲/非同步配合 Task.Run/BeginInvoke 易於實現需自行建立執行緒池或用 ExecutorService
序列化事件欄位預設不序列化 ([field:NonSerialized])需自行標記 transient

C# 事件進階模式

1. 標準 EventHandler 模式

public event EventHandler<CustomEventArgs> CustomEvent;
protected virtual void OnCustomEvent(CustomEventArgs e)
    => CustomEvent?.Invoke(this, e);

2. 弱事件模式 (WeakEventManager)

解決「觀察者忘記退訂導致主題無法 GC」問題,WPF/UWP 內建支援。

3. 非同步事件

public event Func<Task> AsyncEvent;
public async Task TriggerAsync() 
{
    var tasks = AsyncEvent?.GetInvocationList()
        .Cast<Func<Task>>()
        .Select(f => f()) ?? Array.Empty<Task>();
    await Task.WhenAll(tasks);
}

總結

C# event/delegate 以語言內建機制實現了觀察者模式的所有核心能力,並提供:

  1. 型別安全:委派簽名替代 Object 參數
  2. 語法極簡:+=/-=/?.Invoke() 三大操作
  3. 多播原生:單一事件支援多訂閱者
  4. 生態整合:Visual Studio 設計器、偵錯器、分析器完整支援

Java 觀察者模式雖具跨語言可移植性,但在 JVM 生態外無優勢。C# 開發者應優先使用 event,僅在跨進程、跨語言、或需精細控制訂閱生命週期時考慮手動實作觀察者介面。

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