//堆代码 duidaima.com 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) //也就是只test.AAA += new ABC(test.A);然后调用test.AAA() { test.A() //只有一个多播,直接调用这一个函数 } else // 如果大于一个多播委托,如示例两个多播 { IntPtr FunPtr=test.A()+test.B(); //函数A和函数B形成了一个新的托管地址 FunPtr();//在新形成的托管地址里面分别调用函数A和函数B }3.内存模型
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))内存:
当它组合之后,形成一个新的地址,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
System.MulticastDelegate:CtorClosed //把对象test对象的field设置为abc System.Delegate:Combine //组合成新的委托,也即函数指针链,如果只有一个多播,则即那一个函数指针 System.Runtime.CompilerServices.CastHelpers.ChkCastClass //进行类型转换非托管:
JIT_WriteBarrier //设置card_table,防止GC标记的时候漏掉5.原理图