C# 委托與事件學習筆記(小白入門版)
一、委托(Delegate)基礎
1. 什么是委托?
委托是 C# 中的引用類型,本質是「方法的容器」—— 可像變量一樣存儲、傳遞方法(實現(xiàn) “把方法當參數(shù)用”)。
核心作用:解耦代碼,實現(xiàn)「回調機制」(如方法 A 調用方法 B)、「事件驅動」(如按鈕點擊響應)。
2. 委托的定義與基本使用
(1)定義委托
用 delegate 關鍵字聲明,需明確返回值類型和參數(shù)列表(即 “方法簽名”,后續(xù)綁定的方法必須完全匹配)。
語法示例:
// 定義委托:返回值void,參數(shù)為1個string
delegate void MessageDelegate(string content);
(2)綁定方法并調用
委托實例可綁定「靜態(tài)方法」或「實例方法」,調用委托即執(zhí)行綁定的方法。
完整代碼示例:
using System;
// 1. 定義委托(簽名:接收string,返回void)
delegate void GreetDelegate(string name);
class GreetSystem
{
// 2. 靜態(tài)方法(無需創(chuàng)建對象即可調用)
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"); // 調用 → 輸出:Hello, Alice!
// 綁定實例方法(先創(chuàng)建對象)
GreetSystem system = new GreetSystem();
GreetDelegate greet2 = system.ChineseGreet;
greet2("Bob"); // 調用 → 輸出:你好,Bob!
}
}
二、委托的實例化方式
委托是引用類型,需創(chuàng)建實例才能使用,有兩種常見方式:
1. 顯式實例化(new 關鍵字)
直觀體現(xiàn) “委托是對象”,適合新手理解原理:
// 顯式創(chuàng)建委托實例,綁定目標方法
GreetDelegate greet = new GreetDelegate(EnglishGreet);
greet("Charlie"); // 調用 → 輸出:Hello, Charlie!
2. 隱式實例化(簡化寫法)
編譯器自動創(chuàng)建實例,日常開發(fā)更常用:
// 簡化寫法(等價于顯式實例化)
GreetDelegate greet = EnglishGreet;
greet("David"); // 調用 → 輸出:Hello, David!
三、多播委托(Multicast Delegate)
一個委托實例可綁定多個方法,調用時按 “添加順序” 依次執(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. 調用委托(依次執(zhí)行所有方法)
Console.WriteLine("觸發(fā)通知:");
notify();
// 輸出:
// ? 發(fā)送郵件通知
// ? 發(fā)送短信通知
// ? 發(fā)送推送通知
// 4. 用 -= 移除指定方法
notify -= SendSMS;
Console.WriteLine("\n移除短信通知后:");
notify();
// 輸出:
// ? 發(fā)送郵件通知
// ? 發(fā)送推送通知
}
}
2. 帶參數(shù)多播委托
核心規(guī)則:
所有綁定的方法必須與委托 “參數(shù)列表完全匹配”(類型、數(shù)量、順序一致);
調用時傳入的參數(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(實際時間以運行時為準)
}
}
2. 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,供外部調用模擬觸發(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
2. 事件的核心特性
安全限制:外部只能用 +=(訂閱)和 -=(取消訂閱),不能直接賦值(如 btn.Click = null)或調用(如 btn.Click()),避免委托被意外修改。
發(fā)布 - 訂閱分離:發(fā)布者(如按鈕)負責觸發(fā)事件,訂閱者(如業(yè)務邏輯)負責處理事件,解耦代碼。
六、學習路線總結
基礎認知:委托是 “方法的引用”→ 掌握定義、綁定、調用;
進階使用:多播委托用 +=/-= 管理多個方法 → 理解參數(shù)共享和返回值特性;
簡化寫法:匿名方法 / Lambda 減少代碼冗余 → 熟練使用 Action/Func 預定義委托;
安全封裝:事件基于委托實現(xiàn)發(fā)布 - 訂閱 → 掌握事件的定義、訂閱和觸發(fā)。
按此順序練習,結合代碼示例調試,可逐步掌握委托與事件的核心用法!