委派是型別安全的函式指標,封裝方法簽名(參數、回傳型別),允許將方法作為參數傳遞、儲存於變數、串聯多播。事件是委派的受限封裝,僅允許 +=/-= 訂閱/取消訂閱,禁止外部直接調用或覆寫。
using System;
class Program {
// 宣告委派型別
delegate int MathOp(int a, int b);
static void Main() {
// 實例化委派,指向靜態方法
MathOp op1 = new MathOp(Add);
MathOp op2 = new MathOp(Multiply);
// 像呼叫方法一樣調用委派
Console.WriteLine(op1(100, 200)); // 300
Console.WriteLine(op2(100, 200)); // 20000
Console.ReadLine();
}
static int Add(int a, int b) => a + b;
static int Multiply(int a, int b) => a * b;
}
delegate int MathOp(int a, int b); 定義簽名契約new MathOp(Add) 建立委派實例,**不加括號**op1(100, 200)MathOp combined = op1 + op2; 依序調用,回傳最後一個結果
using System;
class Program {
static void Main() {
Student s1 = new Student { Name = "Alice" };
Student s2 = new Student { Name = "Bob" };
// 訂閱事件(+= 語法糖)
s1.RegisterOK += Student_RegisterOK;
s2.RegisterOK += Student_RegisterOK;
s1.Register();
s2.Register();
Console.ReadLine();
}
static void Student_RegisterOK() => Console.WriteLine("Hello");
}
class Student {
// 1. 宣告委派型別
public delegate void DelegateRegisterOkEvent();
// 2. 宣告事件(關鍵字 event 限制存取權)
public event DelegateRegisterOkEvent RegisterOK;
public string Name;
public void Register() {
Console.WriteLine("Register Method");
// 3. 觸發事件(null 條件運算子 C# 6+)
RegisterOK?.Invoke();
}
}
| 特性 | public delegate field | public event |
|---|---|---|
| 外部可直接調用 | 是(危險) | 否(編譯錯誤) |
| 外部可覆寫(=) | 是(清除其他訂閱者) | 否(僅 +=/-=) |
| 外部可讀取委派清單 | 是 | 否 |
| 語意明確性 | 低 | 高(發布/訂閱模式) |
class Program {
static void Main() {
Student s1 = new Student { Name = "Alice" };
s1.RegisterOK += Student_RegisterOK;
s1.Register();
Console.ReadLine();
}
// 事件處理器簽名:object sender, EventArgs e
static void Student_RegisterOK(RegisterOkArgs e) {
Console.WriteLine(e.EventInfo);
}
}
class Student {
public delegate void DelegateRegisterOkEvent(RegisterOkArgs e);
public event DelegateRegisterOkEvent RegisterOK;
public string Name;
public void Register() {
Console.WriteLine("Register Method");
// 建立事件參數物件傳遞上下文
RegisterOK?.Invoke(new RegisterOkArgs($"Student Name: {Name}"));
}
}
// 自訂事件參數繼承 EventArgs
class RegisterOkArgs : EventArgs {
public string EventInfo;
public RegisterOkArgs(string info) => EventInfo = info;
}
.NET 定義通用委派:
public delegate void EventHandler(object sender, EventArgs e);
public delegate void EventHandler(object sender, TEventArgs e); // 泛型版
所有符合 (object, EventArgs) 簽名的方法均可作為標準事件處理器(如 button1_Click)。
// Button 類別內部(簡化)
public class Button {
public event EventHandler Click; // 使用標準 EventHandler
protected virtual void OnClick(EventArgs e) {
Click?.Invoke(this, e); // sender = this
}
// 使用者程式碼
button1.Click += (sender, e) => { ... }; // Lambda 訂閱
// 等同於
button1.Click += new EventHandler(button1_Click);
GetInvocationList() 逐個 try-catch
public void SafeInvoke(EventHandler handler, object sender, EventArgs e) {
if (handler == null) return;
foreach (EventHandler h in handler.GetInvocationList()) {
try {
h(sender, e);
} catch (Exception ex) {
Log.Error(ex); // 記錄但不中斷其他處理器
}
}
}
委派是事件的基石,事件是委派的受控暴露。標準模式:delegate 定義簽名 → event 宣告 → ?.Invoke() 觸發 → EventArgs 傳遞上下文。理解這層關係對掌握 C# 事件模型、撰寫安全多播程式碼、除錯事件流至關重要。下篇將深入事件存取器、介面事件、弱事件模式與記憶體洩漏防範。