一、委托(Delegate):类型安全的 "函数指针"
1. 核心概念
委托是 C# 中的一种引用类型,它的作用是 "封装" 一个或多个方法(方法的签名和返回值必须匹配),可以把它理解为一个类型安全的函数指针。你可以把方法当作参数传递、存储或调用,这是实现回调、解耦代码的基础。
2. 基本用法(完整代码示例)
using System;
// 1. 定义委托:确定要封装的方法的签名(返回值+参数)
public delegate void GreetingDelegate(string name);
class Program
{
// 2. 定义匹配委托签名的方法
static void ChineseGreeting(string name)
{
Console.WriteLine($"你好,{name}!");
}
static void EnglishGreeting(string name)
{
Console.WriteLine($"Hello, {name}!");
}
static void Main(string[] args)
{
// 3. 创建委托实例并绑定方法
GreetingDelegate greet1 = ChineseGreeting;
// 调用委托(本质是调用绑定的方法)
greet1("张三"); // 输出:你好,张三!
// 4. 委托的多播(绑定多个方法)
GreetingDelegate greetAll = ChineseGreeting;
greetAll += EnglishGreeting; // += 添加方法
greetAll("李四"); // 依次调用两个方法,输出:你好,李四! Hello, 李四!
// 5. 移除委托中的方法
greetAll -= ChineseGreeting;
greetAll("王五"); // 仅输出:Hello, 王五!
}
}
3. 关键解释
- 委托定义:
public delegate void GreetingDelegate(string name);规定了委托能封装的方法必须是:无返回值、参数为 1 个 string 类型。 - 委托实例化:直接将方法名赋值给委托变量(无需括号),因为委托本质是封装方法。
- 多播委托:通过
+=添加方法、-=移除方法,调用时会按添加顺序执行所有绑定的方法(如果有返回值,仅返回最后一个方法的结果)。 - 常用内置委托:C# 提供了
Action<T>(无返回值)、Func<T,TResult>(有返回值)等内置委托,无需手动定义:// 使用内置Action委托替代自定义GreetingDelegate Action<string> greet = ChineseGreeting; greet("赵六"); // 输出:你好,赵六!二、事件(Event):委托的 "安全封装"
1. 核心概念
事件是基于委托的一种特殊机制,它限制了委托的访问权限:外部类只能通过
+=订阅事件、-=取消订阅,无法直接调用委托或用=覆盖委托(避免意外修改),是实现 "发布 - 订阅" 模式的核心。2. 基本用法(完整代码示例:发布 - 订阅模式)
using System; // 发布者类:触发事件的一方 public class AlarmClock { // 1. 定义委托(事件的"签名") public delegate void RingEventHandler(object sender, EventArgs e); // 2. 定义事件:基于上面的委托 public event RingEventHandler Ring; // 3. 触发事件的方法(通常是protected virtual,供子类重写) protected virtual void OnRing() { // 先判断是否有订阅者,避免空引用异常 Ring?.Invoke(this, EventArgs.Empty); } // 模拟闹钟响铃 public void Start(int seconds) { Console.WriteLine($"闹钟开始计时,{seconds}秒后响铃..."); System.Threading.Thread.Sleep(seconds * 1000); // 模拟等待 OnRing(); // 触发事件 } } // 订阅者类1:响应事件的一方 public class Person { private string _name; public Person(string name) { _name = name; } // 订阅事件的方法(签名必须匹配委托) public void WakeUp(object sender, EventArgs e) { Console.WriteLine($"{_name}:听到闹钟响,起床啦!"); } } // 订阅者类2 public class Cat { public void React(object sender, EventArgs e) { Console.WriteLine("小猫:被闹钟吵醒,喵喵叫!"); } } class Program { static void Main(string[] args) { // 1. 创建发布者(闹钟) AlarmClock clock = new AlarmClock(); // 2. 创建订阅者 Person tom = new Person("Tom"); Cat kitty = new Cat(); // 3. 订阅事件:将订阅者的方法绑定到事件 clock.Ring += tom.WakeUp; clock.Ring += kitty.React; // 4. 启动闹钟(触发事件) clock.Start(2); // 5. 取消订阅 clock.Ring -= kitty.React; Console.WriteLine("\n取消小猫的订阅后,再次响铃:"); clock.Start(1); } }3. 输出结果
闹钟开始计时,2秒后响铃... Tom:听到闹钟响,起床啦! 小猫:被闹钟吵醒,喵喵叫! 取消小猫的订阅后,再次响铃: 闹钟开始计时,1秒后响铃... Tom:听到闹钟响,起床啦!4. 关键解释
- 事件定义:
public event RingEventHandler Ring;事件必须基于委托定义,且通常放在类内部。 - 触发事件:通过
OnXXX()方法触发(命名规范:On + 事件名),用Ring?.Invoke()避免空引用(没有订阅者时委托为 null)。 - 访问限制:外部类无法直接调用
clock.Ring()或clock.Ring = tom.WakeUp,只能用+=/-=,保证了代码安全性。 - 标准事件参数:实际开发中常用
EventHandler<TEventArgs>内置委托,无需自定义委托:// 替换自定义委托,使用内置EventHandler public event EventHandler Ring; // 触发逻辑不变:Ring?.Invoke(this, EventArgs.Empty);三、委托与事件的核心区别
特性 委托(Delegate) 事件(Event) 访问权限 外部可直接调用、覆盖 外部仅能订阅 / 取消订阅 本质 引用类型(函数指针) 委托的封装(特殊语法) 适用场景 灵活的方法封装、回调 发布 - 订阅模式、解耦组件 多播支持 支持(+=/-=) 支持(底层依赖委托多播) 总结
- 委托是封装方法的引用类型,可多播,是事件的基础,核心作用是将方法作为参数传递 / 调用。
- 事件是委托的安全封装,限制了外部对委托的操作,仅开放订阅 / 取消订阅,是实现发布 - 订阅模式的标准方式。
- 实际开发中优先使用
Action<T>/Func<T,TResult>(内置委托)和EventHandler<T>(事件委托),减少自定义委托的代码量。
640

被折叠的 条评论
为什么被折叠?



