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

C# 事件訂閱與撤銷實戰:從 +=/-= 到弱事件模式

     編輯:關於C#

基本訂閱/撤銷語法

public class LoginDialog : Form {
    Button _okBtn, _cancelBtn;

    public LoginDialog() {
        _okBtn = new Button();
        _okBtn.Click += OkButton_Click;   // 訂閱
        _cancelBtn = new Button();
        _cancelBtn.Click += CancelButton_Click;
    }

    void OkButton_Click(object sender, EventArgs e) { /* ... */ }
    void CancelButton_Click(object sender, EventArgs e) { /* ... */ }

    protected override void Dispose(bool disposing) {
        if (disposing) {
            _okBtn.Click -= OkButton_Click;     // 撤銷,防止記憶體洩漏
            _cancelBtn.Click -= CancelButton_Click;
        }
        base.Dispose(disposing);
    }
}

常見陷阱:忘記撤銷導致記憶體洩漏

  • 發布者(事件來源)持有訂閱者委派參考 → 訂閱者無法被 GC 回收。
  • 特別嚴重於靜態事件、長壽命物件發布短壽命物件訂閱的情況。

進階解決方案

1. 弱事件模式

使用 WeakReference 包裝委派,不阻礙 GC。.NET 提供 WeakEventManager (WPF) 或自訂實作。

2. 匿名方法/Lambda 訂閱的撤銷難題

// 錯誤:無法撤銷,因每次產生新委派實例
btn.Click += (s, e) => Console.WriteLine("Click");

// 正確:保存委派參考
EventHandler handler = (s, e) => Console.WriteLine("Click");
btn.Click += handler;
// 稍後
btn.Click -= handler;

3. C# 7.0+ 區域函數解決撤銷問題


void Subscribe() {
    void Handler(object s, EventArgs e) => Console.WriteLine("Click");
    btn.Click += Handler;
    // 需撤銷時
    btn.Click -= Handler;
}

事件觸發的安全檢查

永遠使用 ?.Invoke 或複製委派到區域變數:


// 模式 1 (C# 6+)
Click?.Invoke(this, EventArgs.Empty);

// 模式 2 (舊版相容)
var handler = Click;
if (handler != null) handler(this, EventArgs.Empty);

最佳實務總結

  1. 實作 IDisposable 並在 Dispose 中撤銷所有事件訂閱。
  2. 避免在建構子中訂閱靜態/長壽命事件,改用初始化方法。
  3. 文件化事件引發順序與執行緒上下文。
  4. 單元測試驗證事件正確觸發與撤銷。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved