当前位置: 首页 > news >正文

一、前置基础(MVC学习前提)_核心特性_【C# MVC 前置】委托与事件:从 “小区通知” 看懂 MVC 过滤器的底层逻辑

目录

    • 一、先破抽象:用生活例子看懂委托与事件
      • 1. 委托:像 “中介” 一样帮你找 “干活的人”
      • 2. 事件:像 “小区停水通知” 一样的 “被动响应”
    • 二、委托:从基础到多播,代码一步步来
      • 1. 基础委托:声明→绑定→调用
      • 2. 多播委托:一个中介对接多个工人
    • 三、事件:从发布到订阅,模拟 MVC 里的 “通知逻辑”
      • 1. 事件完整示例:发布者→订阅者→触发
    • 四、委托与事件在 MVC 中的核心应用:过滤器
    • 五、新手必踩的 5 个坑,附解决方案
      • 坑 1:委托未初始化就调用,报空引用
      • 坑 2:多播委托中一个方法抛异常,后续方法不执行
      • 坑 3:事件订阅后不取消,导致内存泄漏
      • 坑 4:事件和委托的区别搞混,滥用事件
      • 坑 5:委托签名不匹配,编译报错
    • 六、互动时间
      • 关于“委托与事件”,你目前最卡壳的是?

大家好,我是William_cl。今天咱们聊 C# 里最 “抽象但又最核心” 的两个概念 —— 委托与事件。很多学 MVC 的新手会觉得 “过滤器”“事件驱动” 这些词很绕,但其实它们的底层就是委托与事件,搞懂这俩,MVC 里的拦截器、生命周期钩子都能一通百通。
在这里插入图片描述

一、先破抽象:用生活例子看懂委托与事件

1. 委托:像 “中介” 一样帮你找 “干活的人”

你家要装修,需要找水电工、木工、油漆工,但你不用一个个去联系 —— 找个装修中介,告诉中介 “我要做什么”,中介帮你对接工人。这里的 “中介” 就是委托,“工人” 是具体的方法,“你告诉中介需求” 就是委托绑定方法,“中介安排工人干活” 就是委托调用方法。

2. 事件:像 “小区停水通知” 一样的 “被动响应”

小区物业要停水,会在业主群发通知(发布事件),业主看到后会做准备:有人囤水,有人提前洗澡,有人无所谓(订阅事件并处理)。这里的 “停水通知” 是事件,“物业” 是事件发布者,“业主” 是事件订阅者,“囤水 / 洗澡” 是事件处理方法。关键区别:委托是 “主动找方法”,事件是 “被动等通知”—— 事件本质是 “受限制的委托”,防止外部乱调用。

二、委托:从基础到多播,代码一步步来

1. 基础委托:声明→绑定→调用

先定义一个 “计算” 委托,能指向 “两个 int 做运算” 的方法:

using System;// 1. 声明委托(定义中介的“业务范围”:只能找“输入两个int,返回int”的工人)
delegate int CalculateDelegate(int a, int b);class Program
{// 2. 定义具体方法(工人:加法、乘法)static int Add(int x, int y) => x + y;static int Multiply(int x, int y) => x * y;static void Main(string[] args){// 3. 绑定委托(中介对接工人)CalculateDelegate calcAdd = Add; // 绑定加法CalculateDelegate calcMult = Multiply; // 绑定乘法// 4. 调用委托(中介安排工人干活)int result1 = calcAdd(3, 5);int result2 = calcMult(3, 5);Console.WriteLine($"3+5={result1}"); // 输出:8Console.WriteLine($"3×5={result2}"); // 输出:15}
}

2. 多播委托:一个中介对接多个工人

委托支持用+=绑定多个方法,调用时会按绑定顺序依次执行(像中介一次安排多个工人干活):

static void Main(string[] args)
{// 多播委托:绑定两个方法CalculateDelegate multiCalc = Add;multiCalc += Multiply; // 再加一个乘法// 调用多播委托:会依次执行Add和MultiplyConsole.WriteLine("多播委托结果:");int result = multiCalc(2, 4); // 注意:只返回最后一个方法的结果(乘法的8)// 输出:// 多播委托结果:// (Add执行了,但返回值被覆盖,只拿到Multiply的8)Console.WriteLine(result); // 输出:8
}

三、事件:从发布到订阅,模拟 MVC 里的 “通知逻辑”

MVC 里的 “Action 执行前拦截”“异常触发通知”,本质就是事件。咱们用 “订单支付成功” 模拟这个过程:支付成功后,要触发 “发短信”“更库存” 两个操作。

1. 事件完整示例:发布者→订阅者→触发

using System;// 1. 先定义委托(事件的“协议”:确定通知的格式)
delegate void OrderPaidDelegate(string orderId);// 2. 事件发布者(订单系统:负责发“支付成功”通知)
class OrderSystem
{// 声明事件(用event关键字修饰委托,限制外部只能订阅/取消,不能直接调用)public event OrderPaidDelegate OnOrderPaid;// 模拟“支付成功”操作,触发事件public void PaySuccess(string orderId){Console.WriteLine($"订单{orderId}支付成功,准备通知...");// 触发事件前先判断:有没有订阅者(避免空引用)OnOrderPaid?.Invoke(orderId); // C#6+语法:等同于if(OnOrderPaid!=null) OnOrderPaid(orderId)}
}// 3. 事件订阅者1(短信服务:收到通知发短信)
class SmsService
{// 事件处理方法(要和委托签名一致)public void SendSms(string orderId){Console.WriteLine($"给订单{orderId}的用户发送支付成功短信");}
}// 4. 事件订阅者2(库存服务:收到通知减库存)
class StockService
{public void ReduceStock(string orderId){Console.WriteLine($"订单{orderId}对应的商品库存减少");}
}// 测试代码
class Program
{static void Main(string[] args){// 创建对象OrderSystem orderSys = new OrderSystem();SmsService sms = new SmsService();StockService stock = new StockService();// 订阅事件(给订单系统的OnOrderPaid绑定处理方法)orderSys.OnOrderPaid += sms.SendSms;orderSys.OnOrderPaid += stock.ReduceStock;// 模拟支付成功:触发事件orderSys.PaySuccess("ORDER123456");// 输出结果:// 订单ORDER123456支付成功,准备通知...// 给订单ORDER123456的用户发送支付成功短信// 订单ORDER123456对应的商品库存减少}
}

四、委托与事件在 MVC 中的核心应用:过滤器

MVC 里的Action 过滤器(比如在 Action 执行前做权限校验,执行后做日志记录),底层就是用委托事件实现的。咱们简化一个 MVC 过滤器的逻辑,让你看明白原理:
简化 MVC 过滤器示例

using System;// 1. 定义过滤器委托(协议:Action执行前后的方法格式)
delegate void ActionExecutingDelegate(string actionName); // Action执行前
delegate void ActionExecutedDelegate(string actionName); // Action执行后// 2. 模拟MVC控制器(包含Action和过滤器事件)
class HomeController
{// 过滤器事件(供外部订阅)public event ActionExecutingDelegate OnActionExecuting;public event ActionExecutedDelegate OnActionExecuted;// 模拟一个Index Actionpublic void Index(){// Step1:Action执行前,触发过滤器事件OnActionExecuting?.Invoke("Index");// Step2:执行Action本身的逻辑Console.WriteLine("执行Home/Index Action的业务逻辑(比如查数据)");// Step3:Action执行后,触发过滤器事件OnActionExecuted?.Invoke("Index");}
}// 3. 自定义过滤器(日志过滤器:订阅事件做日志)
class LogFilter
{// Action执行前的日志public void LogBeforeAction(string actionName){Console.WriteLine($"[日志] Action {actionName} 开始执行,时间:{DateTime.Now:HH:mm:ss}");}// Action执行后的日志public void LogAfterAction(string actionName){Console.WriteLine($"[日志] Action {actionName} 执行完成,时间:{DateTime.Now:HH:mm:ss}");}
}// 测试:模拟MVC请求流程
class Program
{static void Main(string[] args){// 创建控制器和过滤器HomeController controller = new HomeController();LogFilter logFilter = new LogFilter();// 订阅过滤器事件(MVC框架会自动帮你做这一步)controller.OnActionExecuting += logFilter.LogBeforeAction;controller.OnActionExecuted += logFilter.LogAfterAction;// 模拟请求Home/Indexcontroller.Index();// 输出结果(完美模拟MVC过滤器流程):// [日志] Action Index 开始执行,时间:14:30:00// 执行Home/Index Action的业务逻辑(比如查数据)// [日志] Action Index 执行完成,时间:14:30:00}
}

这就是 MVC 过滤器的底层逻辑:控制器在关键节点(Action 执行前 / 后)触发事件,过滤器订阅这些事件,从而实现 “拦截” 和 “扩展” 功能。

五、新手必踩的 5 个坑,附解决方案

坑 1:委托未初始化就调用,报空引用

CalculateDelegate calc = null;
calc(3,5); // 报错:未将对象引用设置到对象的实例

原因: 委托没绑定任何方法,是 null。
解决: 调用前判断 null,或用?.Invoke()(C#6+):

if (calc != null) calc(3,5); 
// 或
calc?.Invoke(3,5);

坑 2:多播委托中一个方法抛异常,后续方法不执行

// 故意写一个抛异常的方法
static int Divide(int x, int y) => x / y; CalculateDelegate multiCalc = Add;
multiCalc += Divide; // 绑定除法(如果y=0会抛异常)
multiCalc += Multiply;multiCalc(4, 0); // Divide抛异常,Multiply不会执行

原因: 多播委托按顺序执行,一个方法报错会中断整个调用链。
解决: 拆分成单个委托调用,逐个加 try-catch:

foreach (var method in multiCalc.GetInvocationList())
{try{method.DynamicInvoke(4, 0); // 逐个调用}catch (Exception ex){Console.WriteLine($"方法出错:{ex.Message}");}
}

坑 3:事件订阅后不取消,导致内存泄漏

// WinForm/ASP.NET中常见:页面关闭后,事件还绑定着
public class MyPage : Page
{private OrderSystem _orderSys = new OrderSystem();protected void Page_Load(object sender, EventArgs e){// 订阅事件,但页面关闭时没取消_orderSys.OnOrderPaid += sms.SendSms;}// 忘记写取消订阅的代码// protected void Page_Unload(...) { _orderSys.OnOrderPaid -= sms.SendSms; }
}

原因: 事件发布者(_orderSys)会持有订阅者(MyPage)的引用,即使页面关闭,MyPage也无法被垃圾回收,导致内存泄漏。
解决: 在订阅者销毁时(如页面 Unload、Dispose)取消订阅:

protected void Page_Unload(object sender, EventArgs e)
{_orderSys.OnOrderPaid -= sms.SendSms; // 取消订阅
}

坑 4:事件和委托的区别搞混,滥用事件

// 错误:不需要“通知”的场景用了事件
public event CalculateDelegate MyEvent; 
// 实际只是需要一个“执行计算”的委托,用事件反而麻烦(不能直接赋值,只能+=/-=)

区别总结:

场景用委托还是事件?
主动调用多个方法(如计算、聚合)委托(多播)
被动响应通知(如 MVC 过滤器、按钮点击)事件

坑 5:委托签名不匹配,编译报错

// 委托是int CalculateDelegate(int a, int b)
static string AddString(int x, int y) => $"{x}+{y}={x+y}"; // 返回值是stringCalculateDelegate calc = AddString; // 报错:无法将类型“...AddString”转换为“CalculateDelegate”

原因: 委托的返回值、参数个数 / 类型必须和方法完全一致。
解决: 要么修改方法签名,要么重新定义匹配的委托:

// 重新定义委托(返回值为string)
delegate string CalculateStringDelegate(int a, int b);
CalculateStringDelegate calc = AddString; // 正确

六、互动时间

委托与事件的核心是 “解耦”—— 就像中介帮你对接工人,你不用管工人是谁;MVC 过滤器帮你对接业务逻辑,你不用改 Controller 代码。
你在学 MVC 时,有没有被过滤器的 “执行顺序”“拦截逻辑” 搞晕过?或者在写委托事件时踩过其他坑?欢迎在评论区分享你的经历,我会抽取 2 位读者,送我整理的《MVC 过滤器实战手册》(含 5 个常用过滤器的完整代码)~

下一期咱们聊 “匿名类型与动态类型”,看看它们在 MVC 的 ViewModel 里怎么简化代码,不见不散!

关于“委托与事件”,你目前最卡壳的是?

  • 多播委托的异常处理(一个方法报错,后续全中断)
  • 事件订阅后的内存泄漏(不清楚何时该取消订阅)
  • 委托/事件与MVC过滤器的绑定逻辑(没理清触发顺序)
  • 概念区分(分不清委托和事件的使用场景)
  • 其他(评论区补充你的具体问题)
http://www.dtcms.com/a/486688.html

相关文章:

  • 网站建设年度汇报详情页设计理念怎么写
  • Spring Boot项目中Maven引入依赖常见报错问题解决
  • Android 13 启动的时候会显示一下logo,很不友好
  • seo网站快速排名外包关键词seo排名优化如何
  • Linux防火墙:核心机制与安全实践全解析
  • SX1261IMLTRT射频收发器Semtech赋能远距离物联网连接的低功耗射频芯片IC
  • Flutter---坐标网格图标
  • 邢台做网站多少钱洪泽网站建设
  • 整体设计 逻辑系统程序 之30 定稿V1 之1 含 4 套程序架构、三式模型与时空约束体系
  • 基于单片机的智能洗衣机的设计与实现(论文+源码)
  • 【ComfyUI】SDXL Revision 文本提示实现参考图像概念迁移生成
  • Zabbix 模板、监控项、图形指南
  • 个人如何做网站软件湛江免费制作网站
  • 嘉兴公司网站模板建站建设厅官方网站
  • 【javaFX基础】javaFX文档学习及基础编程实践
  • [c++语法学习]Day10:c++引用
  • iOS App 上架全流程详解:证书配置、打包上传、审核技巧与跨平台上架工具 开心上架 实践
  • C++设计模式_行为型模式_迭代器模式Iterator
  • [iOS] KVC 学习
  • 网站开发中用到的英文单词舅舅建筑网
  • 怎么做交易网站seo整体优化
  • 基于卷积神经网络的苹果叶片病虫害识别系统,resnet50,vgg16,resnet34【pytorch框架,python代码】
  • 【计算机组成原理】第七章:输入/输出系统
  • 深入理解 Linux NUMA:拓扑、分配策略与调优实践
  • logstash常遇问题(logstash Address already in use 5044)
  • 断点调试介绍与使用案例
  • Kafka在美团数据平台的实践
  • 【完整源码+数据集+部署教程】Aura棕榈油果实分割系统: yolov8-seg-C2f-DCNV2-Dynamic
  • 蛋白表达标签:提升重组蛋白研究与生产的关键工具
  • 网站备案编号查询wordpress 集成paypal