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

C# 事件機制深度解析(上):委派基礎與事件標準模式

     編輯:關於C語言

委派:方法的第一類公民

委派是型別安全的函式指標,封裝方法簽名(參數、回傳型別),允許將方法作為參數傳遞、儲存於變數、串聯多播。事件是委派的受限封裝,僅允許 +=/-= 訂閱/取消訂閱,禁止外部直接調用或覆寫。

委派基礎範例


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();
    }
}

事件 vs 委派欄位

特性public delegate fieldpublic event
外部可直接調用是(危險)否(編譯錯誤)
外部可覆寫(=)是(清除其他訂閱者)否(僅 +=/-=)
外部可讀取委派清單是否
語意明確性低高(發布/訂閱模式)

事件參數傳遞:EventArgs 模式


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;
}

標準 EventHandler 模式

.NET 定義通用委派:


public delegate void EventHandler(object sender, EventArgs e);
public delegate void EventHandler(object sender, TEventArgs e);  // 泛型版

所有符合 (object, EventArgs) 簽名的方法均可作為標準事件處理器(如 button1_Click)。

Button 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# 事件模型、撰寫安全多播程式碼、除錯事件流至關重要。下篇將深入事件存取器、介面事件、弱事件模式與記憶體洩漏防範。

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