C#的委托原理刨析and事件原理刨析和两者的比较
什么是委托
委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容参数和返回类型的方法进行绑定。 你可以通过委托实例调用方法。
简单的理解,委托是方法的抽象类,它定义了方法的类型,可以实例化。和普通的类一样,可以申明变量进行赋值,可以当作参数传递,可以定义成属性。
委托具有以下属性:
委托类似于 C++ 函数指针,但委托完全面向对象,不像 C++ 指针会记住函数,委托会同时封装对象实例和方法。
委托允许将方法作为参数进行传递。
委托可用于定义回调方法。
委托可以链接在一起;具备单播、多播功能。
方法不必与委托类型完全匹配。 有关详细信息,请参阅使用委托中的变体。
使用 Lambda 表达式可以更简练地编写内联代码块。 Lambda 表达式(在某些上下文中)可编译为委托类型。
1.委托基础介绍
1.1 delegate委托的声明
使用 delegate 关键字,定义具体的委托类型,Delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型。
查看代码
方法绑定,进行调用
查看代码
1.2 Action 和 Func 背景
抽象的 Delegate 类提供用于松散耦合和调用的基础结构,但是这样看来,引发一个问题,无论何时需要不同的方法参数,这都会创建新的委托类型。 一段时间后此操作可能变得繁琐。 每个新功能都需要新的委托类型,幸运的是,没有必要这样做,框架已经帮我们定义Action 和 Func 类,我们可以直接申明进行使用
1.3 Action<T> 类
Action是无返回值的泛型委托。Action 委托的变体可包含多达 16 个参数,如 Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>。 重要的是这些定义对每个委托参数使用不同的泛型参数,这样可以具有最大的灵活性。框架源码,如图:

使用就很方便了,我们只需要直接申明委托类型进行使用,例:
查看代码
1.4 Func<T> 类
Func 委托的变体可包含多达 16 个输入参数,如 Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>。 按照约定,返回结果的类型始终是所有 Func 声明中最后一个参数的类型,利用out类型参数实现。
Func是有返回值的泛型委托,func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void。框架源码,如下:

使用就很方便了,我们只需要直接申明委托类型进行使用,例:
查看代码
2. 委托实战案例
我这里就做一个多播案例,帮助大家理解,其实.NET core 日志框架和其他第三方日志框架,差不多就是这种套路
2.1 定义Logger类
这个类我们的定义好委托和调用委托的方法。
查看代码
2.2 定义文件记录器
一个写入文件的,文件记录器
查看代码
2.3 定义数据库记录器
一个写入不同数据库的,数据库记录器
查看代码
以上两个代码逻辑,博主就不介绍了,就用一个控制台输出,代表业务代码了
2.4 测试
测试一下,广播和委托删除效果
查看代码
运行效果:

在实际项目中,大家就自行发挥
3. 委托变量捕获
3.1效果演示
说到委托,博主也把这个重要的知识点讲解一下,这个知识点很多人可能不知道或者踩过坑,但掌握了这个知识点其实可以实现一些比较花哨功能。
这里博主就用一个案例进行体现变量捕获,这里代码博主就用 lambda 表达式 进行简写,不太熟悉的可以通过链接跳转进行学习。
逻辑就是,简单的累计一下数量,通过最终的值体现。这里博主分别申明两个整数型变量,通过两个委托分别累计,然后看各自的值。两个委托区别就是传值方式的不同。
查看代码
测试效果:

大家发现没?逻辑代码一下,只是参数传递方式不一样,结果截然不同:
委托1的方式:不改变变量的值,方法之间是不共享这个参数的。这种很容易理解,就和我们调用普通方法一样,变量是值类型,是拷贝了一个副本传给了方法进行使用
委托2的方式:改变变量的值,方法之间是共享这个参数的。这种就像引用类型参数一样,是不是很神奇,难道是利用了ref关键字实现的?
3.2原理刨析
其实没有大家想学的那么神秘,委托之所以使用方式和类无异,是因为它本身就是一个类,只是这个过程的定义由编译器帮我们做了,我们只需要使用C#的语法糖。接下来博主就带大家揭开委托的神秘面纱。
我也给大家画一个简单的编译=》执行的过程

3.2.1 委托真实面貌
博主就简单写了一个委托,然后通过IL DASM工具查看IL代码
查看代码

3.2.2模拟委托调用过程
查看代码

大家发现没,最终的IL代码一模一样。也就说,委托就是编译器帮我们把func编译成一个带invoke函数的func类和生成一个装捕获的变量和函数体的类,然后通过构造函数将对象引用和函数指针(获取指针就是大家所说的把非托管指针压入当前栈)传给func类的实例化。然后最终调用的时候,委托类的invoke函数会去调用真正的函数。就这样完成了对函数的抽象。
3.2.3 委托变量生命周期
现在大家是不是对委托有了一定的理解了,而委托涉及到的捕获变量和参数变量,生命周期就说得通了,也知道为啥委托改变了变量,能通知到原本的变量,因为对变量就行了类的装箱,打包成了一个一个引用类型,那方法外部当然知道变量的值被改变了,因为大家都是拿着引用对象的地址呀。下面做个生命周期小总结:
p变量是普通变量,当方法被销毁时,它就会被销毁。
count2变量是捕获变量,当委托实例被销毁时,它才会被销毁。
4. 事件
其实讲完委托,事件就很容易理解了, 博主就简单讲解一下,如果大家有需要,博主就再写一篇详细的讲解。
事件:实际上,事件是建立在对委托的语言支持之上的一种设计而已。
4.1 事件定义语法
/定义一个委托
4publicdelegatevoiddelegateRun();
5//定义一个事件6publicevent delegateRun eventRun;简单的说,事件可以看作是一个委托类型的变量
4.2委托和事件共性:
它们都提供了一个后期绑定方案:在该方案中,组件通过调用仅在运行时识别的方法进行通信。 它们都支持单个和多个订阅服务器方法。 也就是单播和多播支持。 二者均支持用于添加和删除处理程序的类似语法。 最后,引发事件和调用委托使用完全相同的方法调用语法。 它们甚至都支持与 ?. 运算符一起使用的相同的 Invoke() 方法语法。
4.3 事件原理刨析
publicevent EventHandler<NewMailEventArgs> NewMail; 
可以看到当我们定义一个NewEvent时,编译器帮我们生成了:1. 一个private NewMail 字段,类型为 EventHandler<NewMailEventArgs>。 2.一个 add_NewMail 方法,用于将委托添加到委托链(内部调用了Delegate.Combine方法)。3.一个 remove_NewMail 方法,用于将委托从委托链移除(内部调用了Delegate.Remove方法)。对事件的操作,就是是对NewMail字段的操作。
4.4 如何选择
主要区别就是:
1.事件处理程序通过修改事件参数对象的属性将信息传回到事件源。 虽然这些惯用语可发挥作用,但它们不像从方法返回值那样自然。
2.包含事件的类以外的类只能添加和删除事件侦听器;只有包含事件的类才能调用事件。 事件通常是公共类成员。 相比之下,委托通常作为参数传递,并存储为私有类成员(如果它们全部存储)
3.当事件源将在很长一段时间内引发事件时,基于事件的设计会更加自然。比如基于事件的 UI 控件设计案例
总结:
(1)事件:事件时属于类的成员,所以要放在类的内部。
(2)委托:属于一个定义,是和类、接口类似的,通常放在外部。
所以事件这种架构设计思想还是很值得大家去学习的。
所以说,如果你的代码在不调用任何订阅服务器的情况下可完成其所有工作,使用基于事件的设计会更好点。
大家在项目中,怎么进行选择,就看实际需求了。
相关文章:
C#的委托原理刨析and事件原理刨析和两者的比较
什么是委托委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容参数和返回类型的方法进行绑定。 你可以通过委托实例调用方法。简单的理解,委托是方法的抽象类,它定…...
Redis学习【8】之Redis RDB持久化
文章目录Redis 持久化1 持久化基本原理2 RDB(Redis DataBase) 持久化2.1 持久化的执行2.2 手动 save 命令2.3 手动 bgsave 命令2.4 自动条件触发2.5 查看持久化时间3 RDB 优化配置3.1 save3.2 stop-write-on-bgsave-error3.3 rdbcompression3.4 rdbchecksum3.5 sanitize-dump-p…...
SpringSecurity认证
文章目录登陆校验流程依赖yaml实现建表、工具类、实体类加密器、AuthenticationManager登录逻辑登录过滤器、配置过滤器登出登陆校验流程 认证 登录: ①自定义登录接口 调用ProviderManager的方法进行认证 如果认证通过生成token,根据userId把用…...
Socket套接字
概念 Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。 分类 Socket套接字主要针对传输层协议划分为如下三类: 流套接字:使用传输层TCP…...
mysql详解之innoDB
索引 Mysql由索引组织,所以索引是mysql多重要概念之一。 聚簇索引 InnoDB和MyISAm一样都是采用B树结构,但不同点在于InnoDB是聚簇索引(或聚集索引),将数据行直接放在叶子节点后面。 这里可能存在一个误区࿱…...
电信运营商的新尝试:探索非通信领域的发展
近年来,随着电信运营商竞争的日趋激烈和网络建设的成本不断攀升,许多电信运营商已经开始缩减IT投资。然而,在如此情况下,电信运营商仍然需要寻找新的增长机会。那么,在持续缩减IT投资的情况下,电信运营商可…...
第07章_单行函数
第07章_单行函数 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 1. 函数的理解 1.1 什么是函数 函数在计算机语言的使用中贯穿始终,函数的作用是什么呢?它可以把我们经…...
Echarts实现多柱状图重叠重叠效果
有两种重叠效果: 1. 多个柱子重叠为一个 2. 多个柱子重叠为两组 第一种,图例: 这个灰色不是阴影哦, 是柱子. 1. 使用详解 (1) series.Z 折线图组件的所有图形的 z 值。控制图形的前后顺序。 z 值小的图形会被 z 值大的图形覆盖。z 相比 zlevel 优先级更低,而且不会…...
PHP学习笔记(一谦四益)
前言 上一篇文章 PHP学习笔记(观隅反三)分享了数组的知识,这篇文章接着分享和数组相关的算法。 算法效率 算法效率分为两种:第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,而空间效率被称…...
Jvm -堆对象的划分
堆对于一个jvm进程来说是唯一的,一个进程只有一个jvm,但是进程半酣多个线程,多个线程共享一个堆。 也就是说,一个jvm实例只存在一个堆,同时对也是Java内存管理的核心区域。 Java堆区域的大小在jvm启动时就已经被确定…...
2023美赛F题讲解+数据领取
我们给大家准备了F题的数据,免费领取!在文末 国内生产总值(GDP)可以说是一个国家经济健康状况最著名和最常用的指标之--。它通常用于确定一个国家的购买力和获得贷款的机会,为各国提出提高GDP的政策和项目提供动力。GDP“衡量一个国家在给定时间段内生产…...
【博客625】keepalived开启garp refresh的重要性
keepalived开启garp refresh的重要性 1、场景 1-1、对keepavlied master机器热迁移后出现vip不通,过后恢复 原因:机器迁移后网关那边的arp表没有刷新,流量还是转发到老的端口,但是机器已经迁移到别的端口了,于是网络…...
nginx防护规则,拦截非法字符,防止SQL注入、防XSS,nginx过滤url访问,屏蔽垃圾蜘蛛,WordPress安全代码篇
nginx防护规则,拦截非法字符,防止SQL注入、防XSS,nginx过滤url访问,屏蔽垃圾蜘蛛,WordPress安全代码篇 精心强化,小白一键复制 资源宝分享:www.httple.net 宝塔为例:/www/server/panel/vhost/nginx/你的网站域名.conf,复制代码点击保存 修改www.xx.net你自己域名incl…...
【计算机网络】网络层
文章目录网络层概述网络层提供的两种服务IPv4地址IPv4地址概述分类编址的IPv4地址划分子网的IPv4地址无分类编址的IPv4地址IPv4地址的应用规划IP数据报的发送和转发过程静态路由配置及其可能产生的路由环路问题路由选择路由选择协议概述路由信息协议RIP的基本工作原理开放最短路…...
产品经理知识体系:1.什么是互联网思维?
互联网思维 思考 笔记 用户思维 是要注重用户体验,产品带给用户的价值是什么,是能帮助用户获取想要的商品、解决生活中的问题、获取想要的信息,还是产品能通过兜售参与感、满足感等来满足用户的心理需求。 贯穿产品的整个生命周期过程。 简…...
【数据结构】单链表的接口实现(附图解和源码)
单链表的接口实现(附图解和源码) 文章目录单链表的接口实现(附图解和源码)前言一、定义结构体二、接口实现(附图解源码)1.开辟新空间2.头插数据3.头删数据4.打印整个单链表5.尾删数据6.查找单链表中的数据7…...
TikTok话题量超30亿,这款承载美好记忆的剪贴簿引发讨论
回忆风剪贴簿在TikTok引起关注小超在浏览超店有数后台时发现,有一款平平无奇的剪贴簿的种草视频爆火,在24h内收获了9.9K点赞,播放量更是突破了100W,直接冲到了【种草视频飙升榜】第六名的位置,并且这个数字目前仍在继续…...
了解Dubbo
1.注册中心挂了,消费者还能不能调用生产者? 注册中心挂了, 消费者依然可以调用生产者。生产者和消费者都会在本地缓存注册中心的服务列表,当注册中心宕机时,消费者会读取本地的缓存数据,直接访问生产者&am…...
2023年前端面试知识点总结(JavaScript篇)
近期整理了一下高频的前端面试题,分享给大家一起来学习。如有问题,欢迎指正! 1. JavaScript有哪些数据类型 总共有8种数据类型,分别是Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt Null 代表的含义是空对象…...
jQuery
文章目录jQuery 介绍初体验核心函数jQuery 对象和 dom 对象区分什么是 jQuery 对象,什么是 dom 对象问题:jQuery 对象的本质是什么?jQuery 对象和 Dom 对象使用区别Dom 对象和 jQuery 对象互转(重点)jQuery 选择器&…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
