工作十余年,还是一直被问 委托和事件 有什么区别? 真是够了

一:背景

1. 讲故事

前几天公司一个妹子问我,事件和委托有什么区别?先由衷感叹一下,编码十余年,年轻的时候常被面试官问起,现在年长了,却被后辈们时常问候,看样子逃离编码生涯之前是跑不掉了,不过奇怪的是,这个问题被问起的时候,我发现有很多人用: 事件是一种特殊的委托 来进行总结,是不是挺有意思,我想这句话可能来自于网络上的面试题答案吧,这篇我就试着彻底总结一下。

二:事件真的是特殊的委托吗?

1. 猫和老鼠 经典案例

要想知道两者到底什么关系?先得有一些基础代码,这里就用大家初学事件时用到的 猫和老鼠 经典案例,代码简化如下:

class Program{static void Main(string[] args){Cat cat = new Cat("汤姆");Mouse mouse1 = new Mouse("杰瑞", cat);Mouse mouse2 = new Mouse("杰克", cat);cat.CatComing();Console.ReadKey();}}class Cat{public event Action CatCome;   //声明一个事件private string name;public Cat(string name){this.name = name;}public void CatComing(){Console.WriteLine("猫" + name + "来了");CatCome?.Invoke();}}class Mouse{private string name;public Mouse(string name, Cat cat){this.name = name;cat.CatCome += this.RunAway;        //Mouse 注册 CatCome 主题}public void RunAway(){Console.WriteLine(name + "正在逃跑");}}

代码非常简洁,猫的 CatCome 动作一旦触发,注册到 CatCome 上的 两只 mouse 就会执行各自的逃跑动作 RunAway,如果大家没有看懂可以多看几遍哈。

2. 观察者模式/发布订阅模式

如果你了解过设计模式,我想你应该第一眼就能看出这是 观察者模式,对的,现在无数的框架都在使用这个模式,比如前端的:Vue,Knockout,React,还有redis的发布订阅等等,如果用图画一下大概就是这样。

从图中可以看到,几个 subscribe 都订阅了一个叫做 subject 的主题,一旦有外来的 publish 推送到了 subject,那么订阅 subject 的 subscribe 都会收到通知,接下来根据这张图对刚才的代码再缕一篇:

  • 猫的 public event Action CatCome 就是一个主题 (subject)。

  • 老鼠的 cat.CatCome += this.RunAway 就是 subscribe 对 subject 的订阅。

  • 最后的 public void CatComing() 就是对 subject 的推送, pubish了一条 猫来了

3. 使用观察者模式 对 猫鼠进行解剖

有了观察者模式的基础,对上面的代码进行改造就方便多了, 我可以把 public event Action CatCome; 改成 一个 List<Action> 数组,模拟 Subject 哈,简化后的代码如下:

class Cat{public List<Action> Subject = new List<Action>();   //定义一个主题private string name;public Cat(string name){this.name = name;}public void CatComing(){Console.WriteLine("猫" + name + "来了");Subject.ForEach(item => { item.Invoke(); });}}class Mouse{private string name;public Mouse(string name, Cat cat){this.name = name;cat.Subject.Add(RunAway);    //将 逃跑 方法注入到 subject 中}public void RunAway(){Console.WriteLine(name + "正在逃跑");}}

看到这里,我想你对 事件和委托 应该有一个大概的认识了吧,但这里还有一个问题,C#中的事件 真的如我写的观察者模式这样的吗???要回答这个问题,需要从 IL 角度看一下事件到底生成了什么。

三:从IL角度看事件

1. 使用 ilspy /ildasm 小工具

首先来看一下所谓的事件到底在 IL 层面是个什么东西,如下图:

从图中看其实就是两个接收 Action 参数的 add_CatCome和 remove_CatCome方法,这两个方法简化后的 il 代码如下:


.event [mscorlib]System.Action CatCome
{.addon instance void ConsoleApp2.Cat::add_CatCome(class [mscorlib]System.Action).removeon instance void ConsoleApp2.Cat::remove_CatCome(class [mscorlib]System.Action)
}.method public hidebysig specialnameinstance void add_CatCome (class [mscorlib]System.Action 'value') cil managed
{// Method begins at RVA 0x2090// Code size 41 (0x29).maxstack 3.locals init ([0] class [mscorlib]System.Action,[1] class [mscorlib]System.Action,[2] class [mscorlib]System.Action)IL_0000: ldarg.0IL_0001: ldfld class [mscorlib]System.Action ConsoleApp2.Cat::CatComeIL_0006: stloc.0// loop start (head: IL_0007)IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)IL_0010: castclass [mscorlib]System.ActionIL_0017: ldflda class [mscorlib]System.Action ConsoleApp2.Cat::CatComeIL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.Action>(!!0&, !!0, !!0)// end loopIL_0028: ret
} // end of method Cat::add_CatCome.method public hidebysig specialnameinstance void remove_CatCome (class [mscorlib]System.Action 'value') cil managed
{IL_0000: ldarg.0IL_0001: ldfld class [mscorlib]System.Action ConsoleApp2.Cat::CatComeIL_0006: stloc.0// loop start (head: IL_0007)IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)IL_0010: castclass [mscorlib]System.ActionIL_0017: ldflda class [mscorlib]System.Action ConsoleApp2.Cat::CatComeIL_001e: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.Action>(!!0&, !!0, !!0)IL_0026: bne.un.s IL_0007// end loopIL_0028: ret
} // end of method Cat::remove_CatCome

接下来看看 mouse 类的注册是怎么实现的。

从图中可以看到,所谓的注册就是将 RunAway 作为 add_CatCome 方法的参数传进去而已,回过头来看,最核心的就是那两个所谓的 addxxx 和 removexxx 方法。

2. 将IL代码进行C#还原

可能有些同学对 IL 代码不是很熟悉,如果能还原成 C# 代码就????????了,接下来我就试着还原一下。

class Cat{Action CatCome;public void add_CatCome(Action value){Action action = this.CatCome;Action action2 = null;do{action2 = action;Action value2 = (Action)Delegate.Combine(action2, value);action = Interlocked.CompareExchange(ref this.CatCome, value2, action2);}while ((object)action != action2);}public void remove_CatCome(Action value){Action action = this.CatCome;Action action2 = null;do{action2 = action;Action value2 = (Action)Delegate.Remove(action2, value);action = Interlocked.CompareExchange(ref this.CatCome, value2, action2);}while ((object)action != action2);}private string name;public Cat(string name){this.name = name;}public void CatComing(){Console.WriteLine("猫" + name + "来了");CatCome?.Invoke();}}class Mouse{private string name;public Mouse(string name, Cat cat){this.name = name;cat.add_CatCome(this.RunAway);}public void RunAway(){Console.WriteLine(name + "正在逃跑");}}

可以看出还原后的C#代码跑起来是没有问题的,和观察者模式相比,这里貌似没有看到 subject 这样的 List<Action> 集合,但是你仔细分析的话,其实是有的,你一定要着重分析这句代码: Action value2 = (Action)Delegate.Combine(action2, value); 它用的就是多播委托,用 Combine 方法将后续的 Action 送到前者Action的 _invocationList 中,不信的话,我调试给你看哈。

没毛病吧, Action CatCome 中已经有了两个 callback 方法啦,一旦 CatCome.Invoke(), _invocationList 中的方法就会被执行,也就看到两只老鼠在逃跑啦。

四:总结

您现在是不是明白啦,委托和事件的关系 好比 砖头和房子的关系,房子只是砖头的一个应用场景,您如果说房子是一种特殊的砖,这句话品起来是不是有一种怪怪的感觉,不是吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308343.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7-47 打印选课学生名单 (25 分)(两种做法)(思路加详解+map+vector做法+最后一个点超时解决)+兄弟们冲丫丫

一&#xff1a;题目 假设全校有最多40000名学生和最多2500门课程。现给出每个学生的选课清单&#xff0c;要求输出每门课的选课学生名单。 输入格式: 输入的第一行是两个正整数&#xff1a;N&#xff08;≤40000&#xff09;&#xff0c;为全校学生总数&#xff1b;K&#xf…

SwaggerUI看烦了,IGeekFan.AspNetCore.Knife4jUI 帮你换个新皮肤

背景好像是上周四&#xff0c;看到微信群有人说java有轮子swagger-bootstrap-ui&#xff0c;而c#&#xff0c;就是找不到。于是我一看&#xff0c;就说大话&#xff1a;“这个只是一套UI&#xff0c;他这个有开源地址么”被at说:你试试...当天晚上就把swagger-ui, Knife4j,Swas…

7-48 银行排队问题之单窗口“夹塞”版 (30 分)(思路和详解+map做法)来呀Baby!

一&#xff1a;题目 排队“夹塞”是引起大家强烈不满的行为&#xff0c;但是这种现象时常存在。在银行的单窗口排队问题中&#xff0c;假设银行只有1个窗口提供服务&#xff0c;所有顾客按到达时间排成一条长龙。当窗口空闲时&#xff0c;下一位顾客即去该窗口处理事务。此时如…

7-49 打印学生选课清单 (25 分)(思路+详解+map做法(一对多)+超时解决)Come baby!

一&#xff1a;题目 假设全校有最多40000名学生和最多2500门课程。现给出每门课的选课学生名单&#xff0c;要求输出每个前来查询的学生的选课清单。 输入格式: 输入的第一行是两个正整数&#xff1a;N&#xff08;≤40000&#xff09;&#xff0c;为前来查询课表的学生总数&…

记一次批量处理数据库中的敏感信息

前言 对于一些敏感数据&#xff0c;往往会对其加密后再入库&#xff0c;这个是对数据安全性的一个最为简单的措施。最常见的莫过于手机号码和身份证号了&#xff0c;相信还是有不少公司对这些敏感信息是明文存储的。万一被别人发现系统漏洞&#xff0c;或者是被拖库&#xff0c…

7-50 畅通工程之局部最小花费问题 (35 分)(思路加详解)来呀兄弟们冲呀呀呀呀呀呀呀

一&#xff1a;题目 某地区经过对城镇交通状况的调查&#xff0c;得到现有城镇间快速道路的统计数据&#xff0c;并提出“畅通工程”的目标&#xff1a;使整个地区任何两个城镇间都可以实现快速交通&#xff08;但不一定有直接的快速道路相连&#xff0c;只要互相间接通过快速…

十多位全球技术专家,为你献上近十个小时的.Net微服务介绍

.Net Conf: Focus on Microservices 是 .Net Conf 社区在 2020 年 7 月 30 日举办的线上分享活动。整个活动视频长达近 10 个小时。今天我们来看看都发生了什么。章节汇总本次分享由十多位来自全球的资深技术专家在线分享&#xff0c;涵盖了当前 .Net 在微服务领域的利器。包括…

5G发展是绵绵秋雨 应循序渐进

现阶段5G技术成熟度有待提升当下&#xff0c;在行业内5G基站的短板被调侃为“覆盖、成本、功耗三个3”&#xff0c;也就是3倍成本&#xff0c;3倍功耗、1/3覆盖。就功耗来说&#xff0c;5G基站是4G基站功耗的3倍左右。中国移动董事长杨杰就在GTI国际产业峰会表示&#xff0c;“…

7-52 两个有序链表序列的交集 (20 分)(思路加详解尾插法)come Boby!

一&#xff1a;题目 已知两个非降序链表序列S1与S2&#xff0c;设计函数构造出S1与S2的交集新链表S3。 输入格式: 输入分两行&#xff0c;分别在每行给出由若干个正整数构成的非降序序列&#xff0c;用−1表示序列的结尾&#xff08;−1不属于这个序列&#xff09;。数字用空…

明源云创CI/CD技术演进

源宝导读&#xff1a;在敏捷迭代的过程中需要能够快速的把开发的代码集成打包部署到各个环节对应的环境中。为了高效稳定的完成这个工作&#xff0c;我们引入了DevOps实践理论&#xff0c;并形成了配套的CI/CD工具。本文将介绍云创的CI/CD工具如何演进的过程。一 、传统构建在最…

7-51 两个有序链表序列的合并 (20 分)(vector做法)

一 &#xff1a;题目 、已知两个非降序链表序列S1与S2&#xff0c;设计函数构造出S1与S2合并后的新的非降序链表S3。 输入格式: 输入分两行&#xff0c;分别在每行给出由若干个正整数构成的非降序序列&#xff0c;用−1表示序列的结尾&#xff08;−1不属于这个序列&#xff…

linux qt4卸载,linux卸载QT4和安装QT5的方法

由于项目中需要用到QT的程序&#xff0c;因此安装QT5的软件支持库和QT开发环境是必须的&#xff1a;apt-get insatall qt5-defaultapt-get insatall qt-creatorQT5的库和QT Creator开发环境是独立运行的&#xff0c;如果QT Creator缺少QT5的库则程序写好了没法编译&#xff1b;…

7-53 两个有序序列的中位数 (25 分)(思路加详解)用STL容器中的set容器的自动去重过不去

一&#xff1a;题目 已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列A 0 ​ ,A 1 ​ ,⋯,A N−1 ​ 的中位数指A (N−1)/2 ​ 的值,即第⌊(N1)/2⌋个数&#xff08;A 0 ​ 为第1个数&#xff09;。 输入格式: 输入分三行。第一行给出序列的公共长…

Blazor带我重玩前端(五)

概述本文主要讨论Blazor事件内容&#xff0c;由于blazor事件部分很多&#xff0c;所以会分成上下两篇&#xff0c;本文为第一篇&#xff0c;后续会有第二篇。我们可以视组件是一个类&#xff0c;我们先看一下前文所说的Index.Razor页面生成的C#代码。在此&#xff0c;先补充一下…

关于TensorFlow开发者证书,你想要的资源都在这里!

今天是TensorFlow开发者证书的一个里程碑&#xff0c;全球已经有500位开发者通过考试并顺利拿到了 TensorFlow Certificate。我也有幸在各位大佬的指点下&#xff0c;顺利通过考试&#xff0c;成为国内第7位拿到 TensorFlow Certificate 的开发者。按照Google官网的数据&#…

Kubernetes 在知名互联网公司的(dotnet)落地实践

容器化背景本来生活网&#xff08;benlai.com&#xff09;是一家生鲜电商平台&#xff0c;公司很早就停止了烧钱模式&#xff0c;开始追求盈利。既然要把利润最大化&#xff0c;那就要开源节流&#xff0c;作为技术可以在省钱的方面想想办法。我们的生产环境是由 IDC 机房的 10…

Java银行开户,取钱,存钱,查询余额,退出。。。。。

一&#xff1a;上码 package com.wyj.two;import java.util.Scanner;/*** 封装的练习*/ public class Demo8 {public static void main(String[] args) {Scanner in new Scanner(System.in);Account account new Account();System.out.println("欢迎来到杰哥银行"…

linux开发亿连手机互联,亿连手机互联车载版下载-亿连手机互联车机版v6.6.1 安卓版-腾牛安卓网...

亿连手机互联车机版&#xff0c;交互一体&#xff0c;手机-导航仪应用深度融合&#xff1b;升级服务&#xff0c;依托手机OTA升级导航仪应用&#xff1b;流畅连接&#xff0c;双通道互联技术连接更流畅&#xff1b;全新界面&#xff0c;配合前装和后装专业市场&#xff1b;为您…

7-3 树的同构 (25 分)(思路加详解)来呀baby!!!!!!!!

一&#xff1a;题目 7-3 树的同构 (25 分) 给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2&#xff0c;则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的&#xff0c;因为我们把其中一棵树的结点A、B、G的左右孩子互换后&#xff0c;就得到另外一棵树…

Dapr微服务应用开发系列0:概述

题记&#xff1a;Dapr是什么&#xff0c;Dapr包含什么&#xff0c;为什么要用Dapr。Dapr是什么Dapr&#xff08;Distributed Application Runtime&#xff09;&#xff0c;是微软Azure内部创新孵化团队的一个开源项目&#xff0c;皆在解决微服务应用开发过程的一些共性问题。以…