C#/.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.原理图
多播委托原理如下图所示:
单个委托实际上就是调用函数指针,而多个委托,则是通过多播委托组合单个委托形成一个新的托管函数,在这个托管函数里面进行单个函数一一调用。
结尾
作者:江湖评谈
关注公众号:jianghupt。后台回复:dotnet7。获取一套.Net7 CLR源码教程。
C#/.Net的多播委托到底是啥?彻底剖析下的更多相关文章
- 【C#进阶】多播委托和委托数组像是一回事~
这个MathOperation类有三静态方法,参数都是double,并且没有返回值,考虑用Action<>() 这种预定义的委托哦 class MathOperations { publi ...
- 委托Delegate,多播委托和委托链
定义一个委托 public delegate void CalculateDelegate(int 32 x,int 32 y); 定义一个委托类型的变量 public static Calculat ...
- C# 注销掉事件,解决多播委托链表的问题
c#的事件是多播委托.当绑定多个事件时,事件会依次触发,清除掉注册的委托链表:方法1 C# Code 12345678910111213141516171819202122232425262728 ...
- 【小白学C#】谈谈C#多播委托因异常而终止的解决方案
一.前言 前几天,马三在与朋友闲聊技术的时候,朋友忽然抛出一个问题,把马三难倒了,本着求知的精神,回来以后马三就查阅了相关资料并做了一些实验,终于把问题搞明白了,因此写下本篇博客记录一下.首先,问题是 ...
- C# 委托链(多播委托)
委托既可以封装一个方法,又可以对同一类型的方法进行封装,它就是多播委托 using System; using System.Collections.Generic; using System.Lin ...
- 多播委托和匿名方法再加上Lambda表达式
多播委托就是好几个方法全都委托给一个委托变量 代码: namespace 委托 { class Program { static void math1() { Console.WriteLine(&q ...
- ios多播委托
在现实中回调的需求也分两种 一对一的回调. 一对多的回调. 对于一对一的回调,在IOS中使用delegate.block都能实现.而一对多的回调基本就是通知中心了. 假如现在有一个需求,我们以图片下载 ...
- 委托、多播委托(MulticastDelegate)
委托.多播委托(MulticastDelegate) 多播委托(MulticastDelegate)继承自 Delegate ,表示多路广播委托:即,其调用列表中可以拥有多个元素的委托.实际上,我们自 ...
- 委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理
委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链.本篇的话题包括:委托链是怎样形成的,如何调用委托链方法,以及委托链异常处理. □ 调用返回类型为 ...
- 委托、多播委托、泛型委托Func,Action,Predicate,ExpressionTree
当试图通过一个事件触发多个方法,抽象出泛型行为的时候,或许可以考虑使用委托. 通过委托构造函数或委托变量把方法赋值给委托 private delegate double DiscountDel ...
随机推荐
- 企业什么喜欢做电视看板,电视看板浏览网页的必备工具 电视看板浏览器 电视看板自动打开网页 电视看板必备APP
企业喜欢做电视看板主要是因为它可以提供以下几个方面的优势: 增强企业形象:电视看板可以将企业的信息和广告以更加生动.直观的方式呈现出来,提高企业形象和知名度. 提高工作效率:电视看板可以在企业内部作为 ...
- [Windows/Linux]Linux下的正斜杠"/"和"\"的区别 [转载]
执行某一条Linux命令时,遇到了此问题,甚为不解.[文由] 本篇属于全文转载自: Linux下的正斜杠"/"和""的区别 - 博客园 >>> ...
- [Java EE]辨析: POJO(PO / DTO / VO) | BO/DO | DAO
概念不清,会很影响开发中的逻辑性和条理性,进而影响接口设计,代码编写的质量. 网络上大家对这些个概念的探究很多,但终究没有一个统一的说法. 不论哪家解释,我觉得最重要的是: 1)词汇之间的解释统一: ...
- day13:迭代器&高阶函数(map,reduce,filter,sorted)
迭代器 1.迭代器的定义: 能被next调用,并不断返回下一个值的对象,叫做迭代器(对象) 2.迭代器的概念: 迭代器指的是迭代取值的工具,迭代是一个重复的过程, 每次重复都是基于上一次的结果而继续的 ...
- okio中数据存储的基本单位Segment
1.Segment是Buffer缓冲区存储数据的基本单位,每个Segment能存储的最大字节是8192也就是8k的数据 /** The size of all segments in bytes. * ...
- VMware Workstation Pro许可证
永久许可证:ZC10K-8EF57-084QZ-VXYXE-ZF2XF 备用许可证: UF71K-2TW5J-M88QZ-8WMNT-WKUY4 AZ7MK-44Y1J-H819Z-WMYNC-N7A ...
- python选出一定数量的随机文件到某个文件夹
import os import random import shutil def move_file(target_path, save_path, number): file_list = os. ...
- YII框架(1.7&2.0基础版&2.0高级版)应用程序模板安装方法
YII1.7 安装方法: ① 鼠标右键我的电脑图标-> 选择弹出窗的"属性"选项-->点击"高级"选项卡->在选项卡下面找到"环境变 ...
- springboot项目 宿舍管理系统 (源码+数据库文件+1w字论文+ppt)
来了就点个赞再走呗,即将毕业的兄弟有福了文章底部获取源码springboot项目 宿舍管理系统 (源码+数据库文件+1w字论文+ppt)技术框架:java+springboot+vue+mysql后端 ...
- ArcGIS切片服务获取切片方案xml文件(conf.xml)
在使用ArcGIS进行影像.地形等切片时,往往需要保持一致的切片方案才能够更好的加载地图服务. 本文介绍如何获取已经发布好的ArcGIS服务的切片方案xml文件. 当然切片xml文件还可以通过工具Ge ...