C# 委托与事件学习笔记(小白入门版)
一、委托(Delegate)基础
1. 什么是委托?
委托是 C# 中的引用类型,本质是「方法的容器」—— 可像变量一样存储、传递方法(实现 “把方法当参数用”)。
核心作用:解耦代码,实现「回调机制」(如方法 A 调用方法 B)、「事件驱动」(如按钮点击响应)。
2. 委托的定义与基本使用
(1)定义委托
用 delegate 关键字声明,需明确返回值类型和参数列表(即 “方法签名”,后续绑定的方法必须完全匹配)。
语法示例:
// 定义委托:返回值void,参数为1个string
delegate void MessageDelegate(string content);
(2)绑定方法并调用
委托实例可绑定「静态方法」或「实例方法」,调用委托即执行绑定的方法。
完整代码示例:
using System;
// 1. 定义委托(签名:接收string,返回void)
delegate void GreetDelegate(string name);
class GreetSystem
{
// 2. 静态方法(无需创建对象即可调用)
static void EnglishGreet(string name)
{
Console.WriteLine($"Hello, {name}!");
}
// 3. 实例方法(需先创建类对象)
void ChineseGreet(string name)
{
Console.WriteLine($"你好,{name}!");
}
static void Main()
{
// 绑定静态方法
GreetDelegate greet1 = EnglishGreet;
greet1("Alice"); // 调用 → 输出:Hello, Alice!
// 绑定实例方法(先创建对象)
GreetSystem system = new GreetSystem();
GreetDelegate greet2 = system.ChineseGreet;
greet2("Bob"); // 调用 → 输出:你好,Bob!
}
}
二、委托的实例化方式
委托是引用类型,需创建实例才能使用,有两种常见方式:
1. 显式实例化(new 关键字)
直观体现 “委托是对象”,适合新手理解原理:
// 显式创建委托实例,绑定目标方法
GreetDelegate greet = new GreetDelegate(EnglishGreet);
greet("Charlie"); // 调用 → 输出:Hello, Charlie!
2. 隐式实例化(简化写法)
编译器自动创建实例,日常开发更常用:
// 简化写法(等价于显式实例化)
GreetDelegate greet = EnglishGreet;
greet("David"); // 调用 → 输出:Hello, David!
三、多播委托(Multicast Delegate)
一个委托实例可绑定多个方法,调用时按 “添加顺序” 依次执行所有方法(用 += 添加、-= 移除)。
1. 无参数多播委托
代码示例:
using System;
// 定义无参数、无返回值的委托
delegate void NotifyDelegate();
class NotificationSystem
{
static void SendEmail() => Console.WriteLine("✅ 发送邮件通知");
static void SendSMS() => Console.WriteLine("✅ 发送短信通知");
static void SendPush() => Console.WriteLine("✅ 发送推送通知");
static void Main()
{
// 1. 绑定第一个方法
NotifyDelegate notify = SendEmail;
// 2. 用 += 添加其他方法
notify += SendSMS;
notify += SendPush;
// 3. 调用委托(依次执行所有方法)
Console.WriteLine("触发通知:");
notify();
// 输出:
// ✅ 发送邮件通知
// ✅ 发送短信通知
// ✅ 发送推送通知
// 4. 用 -= 移除指定方法
notify -= SendSMS;
Console.WriteLine("\n移除短信通知后:");
notify();
// 输出:
// ✅ 发送邮件通知
// ✅ 发送推送通知
}
}
2. 带参数多播委托
核心规则:
所有绑定的方法必须与委托 “参数列表完全匹配”(类型、数量、顺序一致);
调用时传入的参数,会共享给所有绑定的方法。
代码示例:
using System;
// 定义带1个string参数的委托
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;
// 传入参数,所有方法共享
log("系统启动成功(2025-10-21 10:00)");
// 输出:
// [控制台] 系统启动成功(2025-10-21 10:00)
// [本地文件] 系统启动成功(2025-10-21 10:00)
// [远程服务器] 系统启动成功(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. 带参数的Lambda(匹配PrintDelegate)
PrintDelegate print1 = msg =>
{
Console.WriteLine($"Lambda输出:{msg}");
};
print1("测试带参数Lambda"); // 输出:Lambda输出:测试带参数Lambda
// 2. 无参数Lambda(匹配Action委托)
Action print2 = () => Console.WriteLine("Lambda输出:无参数示例");
print2(); // 输出:Lambda输出:无参数示例
// 3. 带返回值的Lambda(匹配Func委托)
Func<int, int> square = num => num * num;
Console.WriteLine($"5的平方:{square(5)}"); // 输出:5的平方:25
}
}
五、事件(Event)基础
事件是「委托的安全封装」,基于委托实现 “发布 - 订阅” 模式(如 UI 按钮点击、消息通知),限制外部对委托的直接修改,更安全。
1. 事件的定义与使用
完整代码示例:
using System;
// 1. 定义事件对应的委托(标准签名:sender=事件源,e=事件数据)
delegate void ButtonClickHandler(object sender, EventArgs e);
// 2. 事件发布者(如按钮控件)
class CustomButton
{
// 定义事件(用event关键字,基于委托)
public event ButtonClickHandler Click;
// 触发事件的方法(通常为public,供外部调用模拟触发)
public void OnClick()
{
// 先判断是否有订阅者(避免空引用异常)
if (Click != null)
{
Click(this, EventArgs.Empty); // 触发事件
}
// 简化写法(C# 6.0+ 空合并运算符)
// Click?.Invoke(this, EventArgs.Empty);
}
}
// 3. 事件订阅者(如业务逻辑类)
class Program
{
static void Main()
{
// 创建按钮实例(发布者)
CustomButton btn = new CustomButton();
// 订阅事件(用+=绑定处理方法)
btn.Click += Btn_Click1;
btn.Click += Btn_Click2;
// 模拟按钮点击(触发事件)
Console.WriteLine("点击按钮:");
btn.OnClick();
}
// 事件处理方法1
static void Btn_Click1(object sender, EventArgs e)
{
Console.WriteLine("处理方法1:按钮被点击,执行逻辑A");
}
// 事件处理方法2
static void Btn_Click2(object sender, EventArgs e)
{
Console.WriteLine("处理方法2:按钮被点击,执行逻辑B");
}
}
// 输出:
// 点击按钮:
// 处理方法1:按钮被点击,执行逻辑A
// 处理方法2:按钮被点击,执行逻辑B
2. 事件的核心特性
安全限制:外部只能用 +=(订阅)和 -=(取消订阅),不能直接赋值(如 btn.Click = null)或调用(如 btn.Click()),避免委托被意外修改。
发布 - 订阅分离:发布者(如按钮)负责触发事件,订阅者(如业务逻辑)负责处理事件,解耦代码。
六、学习路线总结
基础认知:委托是 “方法的引用”→ 掌握定义、绑定、调用;
进阶使用:多播委托用 +=/-= 管理多个方法 → 理解参数共享和返回值特性;
简化写法:匿名方法 / Lambda 减少代码冗余 → 熟练使用 Action/Func 预定义委托;
安全封装:事件基于委托实现发布 - 订阅 → 掌握事件的定义、订阅和触发。
按此顺序练习,结合代码示例调试,可逐步掌握委托与事件的核心用法!