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

news2024/11/13 9:31:16

前言

委托在.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

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

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

相关文章

Linux 多路转接 —— poll

目录 传统艺能😎poll🤣struct pollfd🤣 poll 服务器😘PollServer类😁运行服务器😒事件处理😁 服务器测试😂 传统艺能😎 小编是双非本科大二菜鸟不赘述,欢迎米…

【初识C语言(4)】操作符

文章目录 1. 算术操作符2. 移位操作符3. 位操作符4. 赋值操作符5. 单目操作符6. 关系操作符7. 逻辑操作符8. 条件操作符9. 逗号表达式10. 下标引用、函数调用和结构成员 1. 算术操作符 - * / %加 减 乘 除 求余 加减乘 都很简单没啥可讲的,这里主要讲解 除 和 求余。…

nginx主配置文件及实操

文章目录 一、nginx主配置文件nginx.conf1.认识nginx服务的主配置文件2.全局配置3.I/O事件配置4.HTTP配置5.检查配置文件是否正确6.浏览器测试 二、实战操作1.nginx的访问状态统计2.基于授权的访问控制:3.基于客户端访问设置(设置…

驱动开发:内核中进程与句柄互转

在内核开发中,经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符(PID)来标识,而句柄是指对内核对象的引用。在Windows内核中,EProcess结构表示一个进程,而HANDLE是一个句柄。 为了实…

【跑实验04】CLIP安装报错,setup.py如何安装,图像编码器(image_encoder)的使用(含源代码)

文章目录 一、如何解决CLIP安装报错二、setup.py如何安装?三、图像编码器(image_encoder)的使用 一、如何解决CLIP安装报错 我第一次尝试安装CLIP,采用的方法是pip的方法: pip install clip但是安装后,无法使用: 明确…

代码随想录二刷 day31 | 贪心之 理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和

day31 理论基础什么是贪心贪心算法的套路一般解题步骤 455.分发饼干376. 摆动序列情况一:上下坡中有平坡情况二:数组首尾两端情况三:单调坡度有平坡 53. 最大子序和 理论基础 什么是贪心 贪心的本质是选择每一阶段的局部最优,从…

vue3基础 ---- 上

目录 一.vue3介绍 1. 官网初识 2.环境搭建 2-1 线上尝试 2-2 CDN使用 2-3 Vue CLI 2-4 Vite 二.vue3基础 1.模板语法 1-1 我的第一个vue应用 1-2 应用背后的真相 1-3 模板语法-新的皮肤来了 1-4 Todolist-来个案例 1-5 点击变心案例 - 是变色 1-6 v-html- 模板…

【libdatachannel】pycharm运行streamer的信令服务及streamer与js客户端本机联调2

后面与ws服务器一直有ping pong2023-06-23 10:30:27.915 VERB [24456] [rtc::impl::PollService::runLoop@178] Entering poll, timeout=10000ms 2023-06-23 10:30:27.916 VERB [24456] [rtc::impl::PollServ

有了电脑怎么搞?我有办法(第一部分)-涵子的个人想法

上一次会选电脑后,今天我们来学习一下电脑的整理方法。今天满满的都是干货,请仔细阅读!注意粗体内容。 目录 一、磁盘太乱咋整? 1.1.磁盘是个啥?为啥乱? 1.2.分区怎么分? 1.3.还是分不清怎…

Java类加载机制:从字节码到对象的奇妙之旅

目录 什么是类加载机制? 类加载顺序 类加载顺序图 双亲委派模型 双亲委派模型示意图 如何打破双亲委派模型? 要想学好java,首先得知道它是什么,怎么运行的,怎么加载的,运行的是个什么东西&#xff0c…

JVM内存模型及JAVA程序运行原理

文章目录 JVM简介JVM的内存结构方法区堆栈程序计数器 JAVA程序在JVM内是如何执行的 JVM简介 JVM------Java Virtual Machine.JVM是Java平台的基础,与实际机器一样,它有自己的指令集(类似CPU通过指令操作程序运行),并在…

Temu、shopee、Lazada、Newegg、美客多销量增长黑科技!

在跨境电商的激烈竞争环境下,2023年伴随着疫情的解封,电商业务重新焕发生机。卖家们不应该只在一个平台投放所有资源,而应多元化地进行平台选择。实际上,许多小型电商平台如Wish、敦煌等也能带来大量业务,而且竞争压力…

如何保证API接口的安全性

API接口的安全性是非常重要的,以下是一些保证API接口安全性的措施: 用户认证、授权:接口的调用者必须提供有效的身份认证信息,包括用户名、密码、密钥等,以保证接口的调用者的身份有效性。同时,需要在接口的…

亚马逊云科技中国峰会:Amazon DeepRacer——因构建 而可见

文章目录 一、前言二、 亚马逊云科技中国峰会大会亮点三、Amazon DeepRacer 赛事火热开启四、Amazon DeepRacer 深度体验五、2023亚马逊云科技中国峰会——Amazon DeepRacerAmazon DeepRacer 中国峰会总决赛Girls in Tech Show高校联动,寻找未来“生力军”Amazon De…

电赛汇总(三):常用传感器电路模块设计

电赛汇总(三):常用传感器电路模块设计 这一章节主要详细记录各种常用的传感器的电子芯片型号、设计原理与思想,以便随时查看翻阅。这部分内容出自黄根春等学者著的《全国大学生电子设计竞赛教程》一书中,感兴趣的朋友可以购买翻阅。 文章目…

Debian12编译安装R软件

1.解压源码包 2.运行./configure --prefix/usr/local/R-4.3 报错,没有安装fortran编译器 3.运行./configure --prefix/usr/local/R-4.3 4.运行./configure --prefix/usr/local/R-4.3 5.运行./configure --prefix/usr/local/R-4.3 6.运行./configure --pref…

厦门大学计算机考研分析

关注我们的微信公众号 姚哥计算机考研 更多详情欢迎咨询 厦门大学(B)考研难度(☆☆☆☆☆) 厦门大学计算机考研主要招生学院有信息学院、人工智能研究院、医学院和电影学院。目前均已出拟录取名单。 厦门大学信息学院&#xff…

14. WebGPU 透视投影

在上一篇文章中,介绍了如何制作 3D ,但 3D 没有任何透视效果。它使用的是所谓的“正交”视图,它有其用途,但通常不是人们说“3D”时想要的。 现在,需要添加透视图。究竟什么是透视?基本特征就是离得越远的…

牛客网基础语法71~80题

牛客网基础语法71~80题😘😘😘 💫前言:今天是咱们第八期刷牛客网上的题目。 💫目标:可以掌握循环嵌套,可以采用一些数组来解决问题,对循环知识掌握熟练,对数学…

H5套壳微信小程序跳转H5以及配置服务器接口域名和业务域名

一、H5套壳微信小程序跳转H5 基本语法&#xff1a; <web-view src"https://你的域名"></web-view> 1. 其中&#xff0c;url必须在管理后台加进业务域名&#xff0c;并且是https开头的。 使用了web-view&#xff0c;页面将不能放置其他元素&#xff0c…