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);
}
}
使用 WeakReference 包裝委派,不阻礙 GC。.NET 提供 WeakEventManager (WPF) 或自訂實作。
// 錯誤:無法撤銷,因每次產生新委派實例
btn.Click += (s, e) => Console.WriteLine("Click");
// 正確:保存委派參考
EventHandler handler = (s, e) => Console.WriteLine("Click");
btn.Click += handler;
// 稍後
btn.Click -= handler;
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);
IDisposable 並在 Dispose 中撤銷所有事件訂閱。