性欧美暴力猛交69hd,玩弄人妻少妇精品视频,无码人妻精品中文字幕,偷拍网,亚洲自拍

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

時(shí)間:2023-06-22 10:22:59       來源:博客園
前言

委托在.Net里面被托管代碼封裝了之后,看起來似乎有些復(fù)雜。但是實(shí)際上委托即是函數(shù)指針,而多播委托,即是函數(shù)指針鏈。本篇來只涉及底層的邏輯,慎入。

概括

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+=的等號(hào)后面每放一個(gè)函數(shù),就相當(dāng)于多了一個(gè)函數(shù)指針。號(hào)稱:多播委托。


【資料圖】

2.多播原理偽代碼以上委托可以簡化成以下偽代碼,其它所有多播委托均可依次類推。

int i;// i表示多播委托的次數(shù)if(i==1) //也就是只test.AAA += new ABC(test.A);然后調(diào)用test.AAA(){   test.A() //只有一個(gè)多播,直接調(diào)用這一個(gè)函數(shù)}else // 如果大于一個(gè)多播委托,如示例兩個(gè)多播{   IntPtr FunPtr=test.A()+test.B(); //函數(shù)A和函數(shù)B形成了一個(gè)新的托管地址   FunPtr();//在新形成的托管地址里面分別調(diào)用函數(shù)A和函數(shù)B}

3.內(nèi)存模型對(duì)象(object)的內(nèi)存,大致是:

為了簡潔,實(shí)質(zhì)非常龐大header+MethodTable+field

委托根據(jù)對(duì)象來,以示例代碼的test對(duì)象為例,test對(duì)象有一個(gè)filed也即是委托類型的變量AAA。AAA則是new ABC得來的。new ABC所實(shí)例化對(duì)象的filed是分別為函數(shù)A,B。那么他們的內(nèi)存模型如下所示:

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

特例:當(dāng)只有一個(gè)多播委托(多播偽代碼里的i==1),類似于以下這種情況:

如果:static void Main(string[] args){   Test test = new Test();   test.AAA += new ABC(test.A);//只有一個(gè)多播   test.AAA(); //test.AAA.Invoke();}那么:test==header+Mehtodtalbe + AAA(test.AAA(1))test.AAA(1)==new ABC(test.A)(header+Methodtable+函數(shù)A(precode,offset:0x18))內(nèi)存:0x000001DB38D552C0  00007ffa3b3654d8 000001db38d55858這里的0x000001DB38D552C0即test的MethodTable地址。000001db38d55858即new ABC(test.A)的MethodTable地址

委托里面只有一個(gè)方法test.A(多播偽代碼里的i==1),這種情況的話,JIT會(huì)直接尋找test.AAA(1)的MethodTable,加上偏移位0x18,也即是函數(shù)test.A的函數(shù)地址。然后運(yùn)行。

注意了,因?yàn)閷?duì)象test只有一個(gè)filed:AAA。超過一個(gè)以上的多播(多播偽代碼里的i!=1,也即else邏輯),它的field是一直變化的,比如new ABC(test.A)的時(shí)候,它的filed是test.AAA(1)。而new ABC(test.B)的時(shí)候,它的field則是test.AAA(2)+test.AAA(1)組合成的托管函數(shù),覆蓋掉前面的。如果有test.AAA(3),那么后面繼續(xù)組合,繼續(xù)覆蓋test對(duì)象的field。

當(dāng)它組合之后,形成一個(gè)新的地址,CLR會(huì)在這個(gè)地址的基礎(chǔ)上加上偏移量0x18(同上特例)進(jìn)行托管函數(shù)代碼調(diào)用。JIT Compile之后,在里面分別調(diào)用函數(shù)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.托管和非托管依次調(diào)用順序,以下函數(shù)按照順序在多播委托中調(diào)用:托管:

System.MulticastDelegate:CtorClosed //把對(duì)象test對(duì)象的field設(shè)置為abcSystem.Delegate:Combine //組合成新的委托,也即函數(shù)指針鏈,如果只有一個(gè)多播,則即那一個(gè)函數(shù)指針System.Runtime.CompilerServices.CastHelpers.ChkCastClass //進(jìn)行類型轉(zhuǎn)換

非托管:

JIT_WriteBarrier //設(shè)置card_table,防止GC標(biāo)記的時(shí)候漏掉

5.原理圖多播委托原理如下圖所示:單個(gè)委托實(shí)際上就是調(diào)用函數(shù)指針,而多個(gè)委托,則是通過多播委托組合單個(gè)委托形成一個(gè)新的托管函數(shù),在這個(gè)托管函數(shù)里面進(jìn)行單個(gè)函數(shù)一一調(diào)用。

結(jié)尾

作者:江湖評(píng)談關(guān)注公眾號(hào):jianghupt。后臺(tái)回復(fù):dotnet7。獲取一套.Net7 CLR源碼教程。

關(guān)鍵詞:

首頁
頻道
底部
頂部