委托在.Net被托管代码包装后,看起来有点复杂。但事实上,委托是函数指针,多播委托是函数指针链。这篇文章只涉及底层逻辑,要谨慎。
示例代码
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 =每次在等号后面放一个函数,就相当于多了一个函数指针。号称:多播委托。
以上多播原理伪代码的委托可以简化为以下伪代码,其他所有多播委托都可以依次类推。
int i;// I表示多播委托的次数
if(i==1) //也就是说,只是testtt,也就是说,只是testttt.AAA = new ABC(test.A);然后调用test.AAA()
{
test.A() ///只有一个多播,直接调用这个函数
}
else // 若大于多播委托,例如,两个多播的例子
{
IntPtr FunPtr=test.A() test.B(); //函数A和函数B形成了新的托管地址
FunPtr();///分别在新形成的托管地址中调用函数A和函数B
}
内存模型对象的内存大致为:
为了简洁,本质是巨大的
header MethodTable field
根据对象,以示例代码的test对象为例。test对象有一个filed,即委托类型的变量AAA。AAA来自NewABC。NewABC实例化对象的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)
特例:当只有一个多播委托时,类似于以下情况:
如果:
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
MethodTable地址为0x00001DB38D552C0,即test。
00001db38d5858 ABC(test.A)Methodtable地址
只有一种方法可以委托testt.A,在这种情况下,JIT将直接寻找test.AAA的MethodTable,再加上偏移位0x18,即函数testt.A函数地址。然后运行。
注意,因为对象test只有一个filed:AAA。超过一个多播,它的field一直在变化,比如newABC,它的filed是testtt.AAA。当newABC时,它的field是test.AAA test.由AAA组成的托管函数覆盖了前面的函数。假如有test.AAA,然后继续组合,继续覆盖test对象的field。
当它组合时,形成一个新的地址,CLR将在此地址的基础上添加偏移0x18来调用托管函数代码。JITCompile后,分别调用函数test.A,test.B,完成委托的多播。参考以下代码:
test.AAA(); //test.AAA.Invoke();
0007FA3AFF7 mov rcx,qword ptr [rbp 28h]
0007FA3AFF7 mov rcx,qword ptr [rcx 8]
0007FFA3AFF7 mov rax,qword ptr [rbp 28h]
0007FA3AFFA3 call qword ptr [rax 18h]
0007FFA3AFFA36 nop
依次调用托管和非托管的顺序,在多播委托中按顺序调用以下函数:托管:
System.MulticastDelegate:CtorClosed ////将对象test对象的field设置为abc
System.Delegate:Combine //组合成新的委托,即函数指针链,如果只有一个多播,也就是函数指针
System.Runtime.CompilerServices.CastHelpers.ChkCastClass //类型转换
非托管:
JIT_WriteBarrier ///设置card_table,防止GC标记在泄漏时泄漏
文章为作者独立观点,不代表天创网配资观点
L.H tiger2023-10-10
博格的这个思路。如果在长期的t+0股票制度的股票市场就可以解决了。所谓的加仓平摊成本的意识。是股市的最大的禁忌。因为。不符合风险投资意识。选择的标的已经大幅度亏损。作为小资金止损是第一位的,然而在中国的股市t+1猪圈制度的长期熏陶桎梏习性下,中国的股市的根本目的是融资,2000年之前还承认股市是投资的。