首页 > 编程语言 >C#/.Net的多播委托到底是啥?彻底剖析下

C#/.Net的多播委托到底是啥?彻底剖析下

时间:2023-06-22 10:22:04浏览次数:45  
标签:多播 ABC AAA 委托 C# test new Net

前言

委托在.Net里面被托管代码封装了之后,看起来似乎有些复杂。但是实际上委托即是函数指针,而多播委托,即是函数指针链。本篇来只涉及底层的逻辑,慎入。


概括

1.示例代码

public delegate void ABC(); //委托写在类的外面
public class Test
{
  public ABC AAA;
  public void A() {  }
  public void B() {  }
}
static void Main(string[] args)
{
   Test test = new Test();
   test.AAA += new ABC(test.A);
   test.AAA += new ABC(test.B);
   test.AAA(); //test.AAA.Invoke();
}

以上的test.AAA+=的等号后面每放一个函数,就相当于多了一个函数指针。号称:多播委托。

2.多播原理伪代码
以上委托可以简化成以下伪代码,其它所有多播委托均可依次类推。

int i;// i表示多播委托的次数
if(i==1) //也就是只test.AAA += new ABC(test.A);然后调用test.AAA()
{
   test.A() //只有一个多播,直接调用这一个函数
}
else // 如果大于一个多播委托,如示例两个多播
{
   IntPtr FunPtr=test.A()+test.B(); //函数A和函数B形成了一个新的托管地址
   FunPtr();//在新形成的托管地址里面分别调用函数A和函数B
}

3.内存模型
对象(object)的内存,大致是:

为了简洁,实质非常庞大
header+MethodTable+field

委托根据对象来,以示例代码的test对象为例,test对象有一个filed也即是委托类型的变量AAA。AAA则是new ABC得来的。new ABC所实例化对象的filed是分别为函数A,B。那么他们的内存模型如下所示:

test==header+Mehtodtalbe + AAA(test.AAA(1) or test.AAA(2)+test.AAA(1))
test.AAA(1)==new ABC(test.A):header+Methodtable+函数A(precode)
test.AAA(2)==new ABC(test.B):header+Methodtable+函数B(precode)

特例:当只有一个多播委托(多播伪代码里的i==1),类似于以下这种情况:

如果:
static void Main(string[] args)
{
   Test test = new Test();
   test.AAA += new ABC(test.A);//只有一个多播
   test.AAA(); //test.AAA.Invoke();
}
那么:
test==header+Mehtodtalbe + AAA(test.AAA(1))
test.AAA(1)==new ABC(test.A)(header+Methodtable+函数A(precode,offset:0x18))

内存:
0x000001DB38D552C0  00007ffa3b3654d8 000001db38d55858
这里的0x000001DB38D552C0即test的MethodTable地址。
000001db38d55858即new ABC(test.A)的MethodTable地址

委托里面只有一个方法test.A(多播伪代码里的i==1),这种情况的话,JIT会直接寻找test.AAA(1)的MethodTable,加上偏移位0x18,也即是函数test.A的函数地址。然后运行。

注意了,因为对象test只有一个filed:AAA。超过一个以上的多播(多播伪代码里的i!=1,也即else逻辑),它的field是一直变化的,比如new ABC(test.A)的时候,它的filed是test.AAA(1)。而new ABC(test.B)的时候,它的field则是test.AAA(2)+test.AAA(1)组合成的托管函数,覆盖掉前面的。如果有test.AAA(3),那么后面继续组合,继续覆盖test对象的field。

当它组合之后,形成一个新的地址,CLR会在这个地址的基础上加上偏移量0x18(同上特例)进行托管函数代码调用。JIT Compile之后,在里面分别调用函数test.A,test.B,完成委托的多播。
参照如下代码:

test.AAA(); //test.AAA.Invoke();
00007FFA3AFF7A27  mov         rcx,qword ptr [rbp+28h]
00007FFA3AFF7A2B  mov         rcx,qword ptr [rcx+8]
00007FFA3AFF7A2F  mov         rax,qword ptr [rbp+28h]
00007FFA3AFF7A33  call        qword ptr [rax+18h]
00007FFA3AFF7A36  nop

4.托管和非托管
依次调用顺序,以下函数按照顺序在多播委托中调用:
托管:

System.MulticastDelegate:CtorClosed //把对象test对象的field设置为abc
System.Delegate:Combine //组合成新的委托,也即函数指针链,如果只有一个多播,则即那一个函数指针
System.Runtime.CompilerServices.CastHelpers.ChkCastClass //进行类型转换

非托管:

JIT_WriteBarrier //设置card_table,防止GC标记的时候漏掉

5.原理图
多播委托原理如下图所示:
image
单个委托实际上就是调用函数指针,而多个委托,则是通过多播委托组合单个委托形成一个新的托管函数,在这个托管函数里面进行单个函数一一调用。


结尾

作者:江湖评谈
关注公众号:jianghupt。后台回复:dotnet7。获取一套.Net7 CLR源码教程。
image

标签:多播,ABC,AAA,委托,C#,test,new,Net
From: https://www.cnblogs.com/tangyanzhi1111/p/17489381.html

相关文章

  • 什么是 ABAP 的 Message Class,Message Number 和 Message Text 试读版
    ABAP编程语言里的Message(消息)是SAP产品里及其重要的一个概念,因为Message是SAP应用在运行过程中,向终端用户提供运行反馈的最重要的交互渠道之一。当用户使用SAP产品过程中,如果遇到各种错误或者提示消息,会根据这些消息,查询文档或者咨询SAP支持人员,以获得下一步的操作......
  • Codeforces 1835F - Good Graph
    goodproblem,badround。判断YES还是NO很trivial,就直接跑最大匹配看看是不是\(n\)即可。如果是NO,那么考虑Hall定理的证明过程构造即可。具体方法就是找到左部任意一非匹配点,在残量网络上BFS可以到达的点,那所有可以到达的左部点形成的集合就是符合要求的反例。因为你......
  • SAP Spartacus 和 Sticky session 相关的话题
    当Commerce后端运行多个Pods/节点时,当连续的请求过快到达时,后端将无法在集群中发送缓存失效通知。此外,如果多个请求分散到多个节点上,会产生延迟和不必要的资源消耗。Spartacus尽可能与单个后端进行交互,以服务于单个客户端。这通常被称为stickysession.Stickysession(粘滞会......
  • 关于技术社区中的 Caveat 用法
    我们在逛StackOverflow社区时,除了学习技术之外,也可以学习老外们专业的英语,比如我今天学到一个新的单词:caveat(警告,附加说明)以前表达这个意思,我都是用note,显然Caveat要更加专业一些。在技术社区中,Caveat(警示)是指在使用某项技术、工具或方法时需要注意的注意事项、限制条件......
  • linux 中 echo命令如何输出感叹号
     001、方法1[root@PC1test01]#echo'abc!'##使用单引号abc! 002、方法2[root@PC1test01]#echo"abc!"##双引号,感叹后后面加空格abc! 003、方法3[root@PC1test01]#echoabc!##什么引号都不加abc! 参考:https://www.cnblogs......
  • 关于使用rsync命令小技巧-交互式输入密码后-再置于后台运行的方法
    在linux系统中,rsync命令常用于在两台linux主机之间同步数据rsync关于认证可以同ssh的用户密码认证一样,我们执行rsync命令后,会要求输入认证的密码,但很多时候数据特别大时,我们是希望执行过程放在后台运行笔者尝试,在执行rsync命令,如 rsync-az/aa/bb/cc/--exclude"dir01/"--ex......
  • CSS3
    CSS的简单介绍如何学习1、css是什么2、css怎么用(快速入门)3、css选择器(重点+难点)4、美化网页(文字、阴影、超链接、列表、渐变)5、盒子模型6、浮动7、定位8、网页动画(特效效果)什么是CSS和CSS的发展史什么是CSSCascadingStyleSheet层叠级联样式表CSS:表现(美化网页)字体,......
  • Javascript
    什么是Javascript概述javaScript是一门世界上最流行的脚本语言Java,JavaScript10天一个合格的后端人员,必须精通JavaScript历史ECMAScript它可以理解为JavaScript的一个标准最新版本已经到es6版本~但是大部分浏览器还只停留在支持es5代码上!开发环境–线上环境,版本不一致......
  • centos6以lvm划分磁盘分区swap/root禁用swap导致重启kernel异常
    目的禁用swap,虚机重启正常envcentos6.9root/swaplvm形式终端kernel异常,导致系统无法正常启动(引导出了问题)步骤正确禁用swap步骤1、sync刷新内存数据到磁盘2、关闭swapswapoff-a(有足够内存,防止内存不够频繁交换空间)3、注释/etc/fstab4、查看grub加载是......
  • 转:Quartz定时调度CronTrigger时间配置格式说明与实例
    转:https://www.iteye.com/blogs/tag/quartz Quartz定时调度CronTrigger时间配置格式说明与实例quartz工作SUN 1、CronTrigger时间格式配置说明CronTrigger配置格式:格式:[秒][分][小时][日][月][周][年]序号说明是否必填允许填写的值......