04-C#.Net-委托和事件-学习笔记

发布时间:2026/5/26 9:23:10

04-C#.Net-委托和事件-学习笔记 一、委托(Delegate)基础1.1 什么是委托委托是一种引用类型本质上是一个类继承自MulticastDelegate。它可以封装方法的引用类似于 C/C 中的函数指针但更加类型安全。// 委托的定义 public delegate void NoReturnNoPara(); public delegate void NoReturnWithPara(int x, int y); public delegate int WithReturnNoPara(); public delegate int WithReturnWithPara(out int x, ref int y);关键点委托定义时需要指定参数类型和返回值类型委托本质是类会被编译器编译成继承自MulticastDelegate的类委托可以定义在类外部或类内部1.2 委托的实例化// 方式1使用 new 关键字 NoReturnNoPara delegate1 new NoReturnNoPara(NoReturnNoParaMethod); // 方式2直接赋值(语法糖) NoReturnNoPara delegate2 NoReturnNoParaMethod; // 方式3使用 Lambda 表达式 NoReturnWithPara delegate3 (x, y) { Console.WriteLine($x{x}, y{y}); };1.3 委托的执行// 方式1使用 Invoke 方法 delegate1.Invoke(); // 方式2直接调用(语法糖) delegate1(); // 异步调用(已过时不推荐) // delegate1.BeginInvoke(null, null);1.4 委托的作用和意义问题场景学生问候方式因地域不同而不同(武汉、上海、广东等)传统方案的问题方案1(枚举switch)业务逻辑耦合方法不稳定方案2(多个独立方法)大量重复代码委托解决方案public delegate void SayHiDelegate(); public void SayHiPerfect(SayHiDelegate sayHiDelegate) { Console.WriteLine(问候前招招手...); sayHiDelegate.Invoke(); // 执行传入的具体问候逻辑 } // 使用 student.SayHiPerfect(student.SayHiWuhan); student.SayHiPerfect(student.SayHiShanghai);优势代码稳定核心方法不需要修改逻辑解耦不同业务逻辑分离逻辑重用去除重复代码二、框架内置委托2.1 Action 委托Action是无返回值的委托最多支持 16 个参数。// 无参数 Action action () Console.WriteLine(Hello); // 有参数 Actionint action1 (x) Console.WriteLine(x); Actionint, string action2 (x, y) Console.WriteLine(${x}, {y}); // 最多16个参数 Actionint, int, int, ..., int action16 (p1, p2, ..., p16) { };2.2 Func 委托Func是有返回值的委托最多支持 16 个输入参数 1 个返回值。// 无参数有返回值 Funcint func () 100; // 有参数有返回值(最后一个泛型参数是返回值类型) Funcint, int func1 (x) x * 2; Funcint, string, int func2 (x, y) x y.Length; // 最多16个输入参数 1个返回值 Funcint, int, ..., int, string func17 (p1, p2, ..., p16) result;为什么要有内置委托统一委托类型避免定义大量自定义委托减少类型数量(委托本质是类)提高代码可读性和互操作性三、多播委托(Multicast Delegate)3.1 概念所有委托都继承自MulticastDelegate可以通过和-操作符注册和移除多个方法。Action action DoNothing; action DoNothingStatic; action new Student().Study; action Student.StudyAdvanced; action () Console.WriteLine(Lambda); // 执行时按注册顺序依次调用 action.Invoke();3.2 移除方法action - DoNothing; // 成功移除 // 注意以下情况无法移除 action - new Student().Study; // 失败不是同一个实例 action - () Console.WriteLine(Lambda); // 失败Lambda 每次生成不同方法移除规则从后往前逐个匹配必须是同一个实例的同一个方法匹配到后移除并停止继续匹配3.3 异步执行多播委托// 多播委托不能直接使用 BeginInvoke // action.BeginInvoke(null, null); // 错误 // 正确做法遍历委托列表 foreach (Action item in action.GetInvocationList()) { item.BeginInvoke(null, null); // 每个方法异步执行 }四、委托的高级应用嵌套委托(俄罗斯套娃)4.1 委托嵌套原理委托可以包装另一个委托形成多层嵌套实现 AOP(面向切面编程)。public delegate void ShowDelegate(); // 核心业务逻辑 ShowDelegate coreMethod () Console.WriteLine(核心业务); // 第一层包装添加日志 ShowDelegate withLog () { Console.WriteLine(前置日志); coreMethod.Invoke(); Console.WriteLine(后置日志); }; // 第二层包装添加性能监控 ShowDelegate withMonitor () { Console.WriteLine(开始监控); withLog.Invoke(); Console.WriteLine(结束监控); }; withMonitor.Invoke();4.2 结合特性实现自动装配public abstract class AbstractMethodAttribute : Attribute { public abstract ShowDelegate Do(ShowDelegate action); } public class BeforeMethodAttribute : AbstractMethodAttribute { public override ShowDelegate Do(ShowDelegate action) { return new ShowDelegate(() { Console.WriteLine(前置处理); action.Invoke(); Console.WriteLine(后置处理); }); } } // 使用 [BeforeWriteLog] [BeforeMethod] public void Method() { Console.WriteLine(核心业务); } // 自动装配执行 ShowDelegate showMethod () methodInfo.Invoke(instance, null); foreach (var attribute in methodInfo.GetCustomAttributes().Reverse()) { showMethod attribute.Do(showMethod); } showMethod.Invoke();应用场景ASP.NET Core 中间件管道日志记录性能监控事务管理权限验证五、事件(Event)5.1 事件的定义事件是特殊的委托使用event关键字修饰。public class Cat { // 委托 public Action MiaoAction null; // 事件 public event Action MiaoEvent null; public void Miao() { Console.WriteLine(喵~); MiaoEvent?.Invoke(); // 只能在类内部触发 } }5.2 委托 vs 事件特性委托事件本质类特殊的委托外部赋值可以赋值只能和-外部调用可以直接调用不能外部调用安全性较低较高子类访问可以不可以Cat cat new Cat(); // 委托可以这样操作 cat.MiaoAction null; // 清空所有订阅 cat.MiaoAction(); // 外部直接触发 // 事件只能这样操作 cat.MiaoEvent Handler; // 只能添加 cat.MiaoEvent - Handler; // 只能移除 // cat.MiaoEvent(); // 错误不能外部触发 // cat.MiaoEvent null; // 错误不能赋值5.3 观察者模式(发布-订阅模式)核心要素发布者(Publisher)定义事件并触发订阅者(Subscriber)订阅事件并响应订阅关系通过建立示例猫叫引发的连锁反应public class Cat { public event Action MiaoEvent; public void Miao() { Console.WriteLine(猫喵~); MiaoEvent?.Invoke(); } } public class Dog { public void Wang() Console.WriteLine(狗汪汪); } public class Mouse { public void Run() Console.WriteLine(老鼠快跑); } // 使用 Cat cat new Cat(); cat.MiaoEvent new Dog().Wang; cat.MiaoEvent new Mouse().Run; cat.Miao(); // 触发所有订阅者的响应优势职责单一猫只负责叫不关心后续反应降低耦合猫不依赖狗、老鼠等类易于扩展添加新的订阅者不需要修改猫的代码六、标准事件模式6.1 标准事件定义.NET 推荐使用EventHandler或EventHandlerTEventArgs定义事件。// 自定义事件参数 public class PriceChangeEventArgs : EventArgs { public int OldPrice { get; set; } public int NewPrice { get; set; } } // 发布者 public class Course { private int _price 5299; // 标准事件定义 public event EventHandlerPriceChangeEventArgs PriceChanged; public int Price { get _price; set { if (value _price) { // 触发事件 PriceChanged?.Invoke(this, new PriceChangeEventArgs { OldPrice _price, NewPrice value }); } _price value; } } } // 订阅者 public class Student { public void OnPriceChanged(object sender, EventArgs e) { var course (Course)sender; var args (PriceChangeEventArgs)e; Console.WriteLine($课程涨价了从 {args.OldPrice} 涨到 {args.NewPrice}); Console.WriteLine(赶紧购买); } } // 使用 Course course new Course(); course.PriceChanged new Student().OnPriceChanged; course.Price 6299; // 触发事件6.2 标准事件模式的特点事件处理器签名void Handler(object sender, EventArgs e)sender事件发布者(谁触发的)e事件参数(携带什么数据)事件参数继承自定义参数类继承EventArgs命名约定事件名动词或动词短语(如PriceChanged、Click)事件参数XxxEventArgs七、核心概念总结7.1 委托的使用场景方法内部业务逻辑耦合严重→ 使用委托解耦多个方法有大量重复代码→ 使用委托实现逻辑重用需要回调机制→ 使用委托传递回调方法需要实现策略模式→ 使用委托传递不同策略7.2 事件的使用场景需要通知多个对象某个动作发生→ 使用事件需要降低类之间的耦合→ 使用事件实现观察者模式UI 编程(如 WinForms、WPF)→ 使用事件处理用户交互需要保护委托不被外部随意调用→ 使用事件增强安全性7.3 记忆口诀委托是盒子可以装方法可以传递多播是链条一个委托串多个方法事件是保险只能订阅不能乱调观察者模式发布订阅解耦神器

相关新闻