委托

C# 委托與事件學習筆記(小白入門版)

一、委托(Delegate)基礎

  1. 什么是委托?
  • 委托是 C# 中的引用類型,本質是「方法的容器」—— 可像變量一樣存儲、傳遞方法(實現(xiàn) “把方法當參數(shù)用”)。
  • 核心作用:解耦代碼,實現(xiàn)「回調(diào)機制」(如方法 A 調(diào)用方法 B)、「事件驅動」(如按鈕點擊響應)。
  1. 委托的定義與基本使用

(1)定義委托

用 delegate 關鍵字聲明,需明確返回值類型和參數(shù)列表(即 “方法簽名”,后續(xù)綁定的方法必須完全匹配)。

語法示例:

// 定義委托:返回值void,參數(shù)為1個string
delegate void MessageDelegate(string content);

(2)綁定方法并調(diào)用

委托實例可綁定「靜態(tài)方法」或「實例方法」,調(diào)用委托即執(zhí)行綁定的方法。

完整代碼示例:

using System;

// 1. 定義委托(簽名:接收string,返回void)
delegate void GreetDelegate(string name);

class GreetSystem
{
    // 2. 靜態(tài)方法(無需創(chuàng)建對象即可調(diào)用)
    static void EnglishGreet(string name)
    {
        Console.WriteLine($"Hello, {name}!");
    }

    // 3. 實例方法(需先創(chuàng)建類對象)
    void ChineseGreet(string name)
    {
        Console.WriteLine($"你好,{name}!");
    }

    static void Main()
    {
        // 綁定靜態(tài)方法
        GreetDelegate greet1 = EnglishGreet;
        greet1("Alice"); // 調(diào)用 → 輸出:Hello, Alice!

        // 綁定實例方法(先創(chuàng)建對象)
        GreetSystem system = new GreetSystem();
        GreetDelegate greet2 = system.ChineseGreet;
        greet2("Bob"); // 調(diào)用 → 輸出:你好,Bob!
    }
}

二、委托的實例化方式

委托是引用類型,需創(chuàng)建實例才能使用,有兩種常見方式:

  1. 顯式實例化(new 關鍵字)

直觀體現(xiàn) “委托是對象”,適合新手理解原理:

// 顯式創(chuàng)建委托實例,綁定目標方法
GreetDelegate greet = new GreetDelegate(EnglishGreet);
greet("Charlie"); // 調(diào)用 → 輸出:Hello, Charlie!
  1. 隱式實例化(簡化寫法)

編譯器自動創(chuàng)建實例,日常開發(fā)更常用:

// 簡化寫法(等價于顯式實例化)
GreetDelegate greet = EnglishGreet;
greet("David"); // 調(diào)用 → 輸出:Hello, David!

三、多播委托(Multicast Delegate)

一個委托實例可綁定多個方法,調(diào)用時按 “添加順序” 依次執(zhí)行所有方法(用 += 添加、-= 移除)。

  1. 無參數(shù)多播委托

代碼示例:

using System;

// 定義無參數(shù)、無返回值的委托
delegate void NotifyDelegate();

class NotificationSystem
{
    static void SendEmail() => Console.WriteLine("? 發(fā)送郵件通知");
    static void SendSMS() => Console.WriteLine("? 發(fā)送短信通知");
    static void SendPush() => Console.WriteLine("? 發(fā)送推送通知");

    static void Main()
    {
        // 1. 綁定第一個方法
        NotifyDelegate notify = SendEmail;
        
        // 2. 用 += 添加其他方法
        notify += SendSMS;
        notify += SendPush;

        // 3. 調(diào)用委托(依次執(zhí)行所有方法)
        Console.WriteLine("觸發(fā)通知:");
        notify(); 
        // 輸出:
        // ? 發(fā)送郵件通知
        // ? 發(fā)送短信通知
        // ? 發(fā)送推送通知

        // 4. 用 -= 移除指定方法
        notify -= SendSMS;
        Console.WriteLine("\n移除短信通知后:");
        notify(); 
        // 輸出:
        // ? 發(fā)送郵件通知
        // ? 發(fā)送推送通知
    }
}
  1. 帶參數(shù)多播委托

核心規(guī)則:

  • 所有綁定的方法必須與委托 “參數(shù)列表完全匹配”(類型、數(shù)量、順序一致);
  • 調(diào)用時傳入的參數(shù),會共享給所有綁定的方法。

代碼示例:

using System;

// 定義帶1個string參數(shù)的委托
delegate void LogDelegate(string logContent);

class LogSystem
{
    static void LogToConsole(string content) => Console.WriteLine($"[控制臺] {content}");
    static void LogToFile(string content) => Console.WriteLine($"[本地文件] {content}");
    static void LogToServer(string content) => Console.WriteLine($"[遠程服務器] {content}");

    static void Main()
    {
        LogDelegate log = LogToConsole;
        log += LogToFile;
        log += LogToServer;

        // 傳入?yún)?shù),所有方法共享
        log("系統(tǒng)啟動成功(2025-10-21 10:00)");
        // 輸出:
        // [控制臺] 系統(tǒng)啟動成功(2025-10-21 10:00)
        // [本地文件] 系統(tǒng)啟動成功(2025-10-21 10:00)
        // [遠程服務器] 系統(tǒng)啟動成功(2025-10-21 10:00)
    }
}

注意事項:

若委托有返回值,多播委托僅保留最后一個方法的返回值(前序方法的返回值會被忽略),因此多播委托通常用于 void 類型。

四、匿名方法與委托

匿名方法是 “無名稱的臨時方法”,直接在委托中定義,適合 “僅使用一次的簡單邏輯”,減少代碼冗余。

  1. 匿名方法綁定委托

代碼示例:

using System;

// 定義委托
delegate void PrintDelegate(string text);

class Program
{
    static void Main()
    {
        // 委托直接綁定匿名方法(用delegate關鍵字定義)
        PrintDelegate print = delegate(string msg)
        {
            Console.WriteLine($"匿名方法輸出:{msg}");
            Console.WriteLine($"當前時間:{DateTime.Now:HH:mm:ss}");
        };

        print("測試匿名方法");
        // 輸出:
        // 匿名方法輸出:測試匿名方法
        // 當前時間:10:30:45(實際時間以運行時為準)
    }
}
  1. Lambda 表達式(匿名方法簡化版)

C# 3.0+ 推薦用 Lambda 表達式,語法更簡潔(本質與匿名方法等價):

using System;

class Program
{
    static void Main()
    {
        // 1. 帶參數(shù)的Lambda(匹配PrintDelegate)
        PrintDelegate print1 = msg => 
        {
            Console.WriteLine($"Lambda輸出:{msg}");
        };
        print1("測試帶參數(shù)Lambda"); // 輸出:Lambda輸出:測試帶參數(shù)Lambda

        // 2. 無參數(shù)Lambda(匹配Action委托)
        Action print2 = () => Console.WriteLine("Lambda輸出:無參數(shù)示例");
        print2(); // 輸出:Lambda輸出:無參數(shù)示例

        // 3. 帶返回值的Lambda(匹配Func委托)
        Func<int, int> square = num => num * num;
        Console.WriteLine($"5的平方:{square(5)}"); // 輸出:5的平方:25
    }
}

五、事件(Event)基礎

事件是「委托的安全封裝」,基于委托實現(xiàn) “發(fā)布 - 訂閱” 模式(如 UI 按鈕點擊、消息通知),限制外部對委托的直接修改,更安全。

  1. 事件的定義與使用

完整代碼示例:

using System;

// 1. 定義事件對應的委托(標準簽名:sender=事件源,e=事件數(shù)據(jù))
delegate void ButtonClickHandler(object sender, EventArgs e);

// 2. 事件發(fā)布者(如按鈕控件)
class CustomButton
{
    // 定義事件(用event關鍵字,基于委托)
    public event ButtonClickHandler Click;

    // 觸發(fā)事件的方法(通常為public,供外部調(diào)用模擬觸發(fā))
    public void OnClick()
    {
        // 先判斷是否有訂閱者(避免空引用異常)
        if (Click != null)
        {
            Click(this, EventArgs.Empty); // 觸發(fā)事件
        }
        // 簡化寫法(C# 6.0+ 空合并運算符)
        // Click?.Invoke(this, EventArgs.Empty);
    }
}

// 3. 事件訂閱者(如業(yè)務邏輯類)
class Program
{
    static void Main()
    {
        // 創(chuàng)建按鈕實例(發(fā)布者)
        CustomButton btn = new CustomButton();

        // 訂閱事件(用+=綁定處理方法)
        btn.Click += Btn_Click1;
        btn.Click += Btn_Click2;

        // 模擬按鈕點擊(觸發(fā)事件)
        Console.WriteLine("點擊按鈕:");
        btn.OnClick();
    }

    // 事件處理方法1
    static void Btn_Click1(object sender, EventArgs e)
    {
        Console.WriteLine("處理方法1:按鈕被點擊,執(zhí)行邏輯A");
    }

    // 事件處理方法2
    static void Btn_Click2(object sender, EventArgs e)
    {
        Console.WriteLine("處理方法2:按鈕被點擊,執(zhí)行邏輯B");
    }
}
// 輸出:
// 點擊按鈕:
// 處理方法1:按鈕被點擊,執(zhí)行邏輯A
// 處理方法2:按鈕被點擊,執(zhí)行邏輯B
  1. 事件的核心特性
  • 安全限制:外部只能用 +=(訂閱)和 -=(取消訂閱),不能直接賦值(如 btn.Click = null)或調(diào)用(如 btn.Click()),避免委托被意外修改。
  • 發(fā)布 - 訂閱分離:發(fā)布者(如按鈕)負責觸發(fā)事件,訂閱者(如業(yè)務邏輯)負責處理事件,解耦代碼。

六、學習路線總結

  1. 基礎認知:委托是 “方法的引用”→ 掌握定義、綁定、調(diào)用;
  2. 進階使用:多播委托用 +=/-= 管理多個方法 → 理解參數(shù)共享和返回值特性;
  3. 簡化寫法:匿名方法 / Lambda 減少代碼冗余 → 熟練使用 Action/Func 預定義委托;
  4. 安全封裝:事件基于委托實現(xiàn)發(fā)布 - 訂閱 → 掌握事件的定義、訂閱和觸發(fā)。

按此順序練習,結合代碼示例調(diào)試,可逐步掌握委托與事件的核心用法!

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 委托delegate 之前學過的任何類型,不管是值類型還是引用類型,其變量都可以進行賦值,或者作為方法的參數(shù)進行傳...
    TALY閱讀 415評論 0 2
  • 本來應該學習泛型與委托的,但是發(fā)現(xiàn)C#這里還沒有系統(tǒng)的記錄過委托與事件,所以先打算把委托與事件補上再繼續(xù)泛型與委托...
    一個有味道的名字閱讀 1,558評論 1 5
  • 前言 上一節(jié)我們了解學習了委托,委托是類型安全的類,它定義了返回類型和參數(shù)的類型。委托類不僅包含對方法的引用,也可...
    實驗屋閱讀 526評論 0 0
  • 一、unity里使用委托常見場景 1、在綁定按鈕事件的使用場景里,如果想批量地綁定按鈕事件,同時想通過綁定事件傳遞...
    GameObjectLgy閱讀 411評論 0 0
  • #1. 委托1.1 聲明委托1.2 使用委托1.3 簡單委托示例1.4 Action<T>和Func<T>委托1....
    MrDecoder閱讀 746評論 0 2

友情鏈接更多精彩內(nèi)容