许昌市网站建设找汉狮,pc优化工具,网站设计制作合同范本,wordpress刷量插件一#xff1a;背景1. 讲故事前几天公司一个妹子问我#xff0c;事件和委托有什么区别#xff1f;先由衷感叹一下#xff0c;编码十余年#xff0c;年轻的时候常被面试官问起#xff0c;现在年长了#xff0c;却被后辈们时常问候#xff0c;看样子逃离编码生涯之前是跑不… 一背景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,KnockoutReact还有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; 改成 一个 ListAction 数组模拟 Subject 哈简化后的代码如下class Cat{public ListAction Subject new ListAction(); //定义一个主题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::CompareExchangeclass [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::CompareExchangeclass [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 这样的 ListAction 集合但是你仔细分析的话其实是有的你一定要着重分析这句代码 Action value2 (Action)Delegate.Combine(action2, value); 它用的就是多播委托用 Combine 方法将后续的 Action 送到前者Action的 _invocationList 中不信的话我调试给你看哈。没毛病吧 Action CatCome 中已经有了两个 callback 方法啦一旦 CatCome.Invoke(), _invocationList 中的方法就会被执行也就看到两只老鼠在逃跑啦。四总结您现在是不是明白啦委托和事件的关系 好比 砖头和房子的关系房子只是砖头的一个应用场景您如果说房子是一种特殊的砖这句话品起来是不是有一种怪怪的感觉不是吗