最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#?中的多態(tài)底層虛方法調(diào)用詳情

 更新時間:2022年06月01日 11:53:28   作者:一線碼農(nóng)聊技術(shù)  
這篇文章主要介紹了C#?中的多態(tài)底層虛方法調(diào)用詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,需要的小伙伴你可以參考一下

前言:

本質(zhì)上來說,CoreCLR 也是 C++ 寫的,所以也逃不過用 虛表 來實(shí)現(xiàn)多態(tài)的玩法, 不過玩法也稍微復(fù)雜了一些,希望本篇對大家有幫助。

一、C# 中的多態(tài)玩法

1. 一個簡單的 C# 例子

為了方便說明,我就定義一個 Person 類和一個 Chinese 類,詳細(xì)代碼如下:

internal class Program
    {
        static void Main(string[] args)
        {
            Person person = new Chinese();

            person.SayHello();

            Console.ReadLine();
        }
    }

    public class Person
    {
        public virtual void SayHello()
        {
            Console.WriteLine("sayhello");
        }
    }

    public class Chinese: Person
    {
        public override void SayHello()
        {
            Console.WriteLine("chinese");
        }
    }
}

2. 匯編代碼分析

接下來用 windbg 在 person.SayHello() 處下一個斷點(diǎn),觀察一下它的反匯編代碼:

internal class Program
    {
        static void Main(string[] args)
        {
            Person person = new Chinese();

            person.SayHello();

            Console.ReadLine();
        }
    }

    public class Person
    {
        public virtual void SayHello()
        {
            Console.WriteLine("sayhello");
        }
    }

    public class Chinese: Person
    {
        public override void SayHello()
        {
            Console.WriteLine("chinese");
        }
    }
}

從匯編代碼看,邏輯非常清晰,大體步驟如下:

(1)eax,dword ptr [ebp-8]

從棧上(ebp-8)處獲取 person 在堆上的首地址,如果不相信的話,可以用 !do 027ea88c 試試看。

0:000> dp ebp-8 L1
0057f300  027ea88c
0:000> !do 027ea88c
Name:        ConsoleApp1.Chinese
MethodTable: 05ce5d3c
EEClass:     05cd3380
Size:        12(0xc) bytes
File:        D:\net6\ConsoleApplication2\ConsoleApp1\bin\x86\Debug\net6.0\ConsoleApp1.dll
Fields:
None

(2)eax,dword ptr [eax]

如果大家了解 實(shí)例 在堆上的內(nèi)存布局的話,應(yīng)該知道,這個首地址存放的就是 methodtable 指針,我們可以用 !dumpmt 05ce5d3c 來驗(yàn)證下。

0:000> dp 027ea88c L1
027ea88c  05ce5d3c

0:000> !dumpmt 05ce5d3c
EEClass:         05cd3380
Module:          05addb14
Name:            ConsoleApp1.Chinese
mdToken:         02000007
File:            D:\net6\ConsoleApplication2\ConsoleApp1\bin\x86\Debug\net6.0\ConsoleApp1.dll
BaseSize:        0xc
ComponentSize:   0x0
DynamicStatics:  false
ContainsPointers false
Slots in VTable: 6
Number of IFaces in IFaceMap: 0

(3)eax,dword ptr [eax+28h]

那這句話是什么意思呢?如果你了解 CoreCLR 的話,你應(yīng)該知道 methedtable 是由一個 class MethodTable 類來承載的,所以它取了 methodtable 偏移 0x28  位置的一個字段,那這個偏移字段是什么呢?我們先用 dt 把 methodtable 結(jié)構(gòu)給導(dǎo)出來。

0:000> dt 05ce5d3c MethodTable
coreclr!MethodTable
   =7ad96bc8 s_pMethodDataCache : 0x00639ec8 MethodDataCache
   =7ad96bc4 s_fUseParentMethodData : 0n1
   =7ad96bcc s_fUseMethodDataCache : 0n1
   +0x000 m_dwFlags        : 0xc
   +0x004 m_BaseSize       : 0x74088
   +0x008 m_wFlags2        : 5
   +0x00a m_wToken         : 0
   +0x00c m_wNumVirtuals   : 0x5ccc
   +0x00e m_wNumInterfaces : 0x5ce
   +0x010 m_pParentMethodTable : IndirectPointer<MethodTable *>
   +0x014 m_pLoaderModule  : PlainPointer<Module *>
   +0x018 m_pWriteableData : PlainPointer<MethodTableWriteableData *>
   +0x01c m_pEEClass       : PlainPointer<EEClass *>
   +0x01c m_pCanonMT       : PlainPointer<unsigned long>
   +0x020 m_pPerInstInfo   : PlainPointer<PlainPointer<Dictionary *> *>
   +0x020 m_ElementTypeHnd : 0
   +0x020 m_pMultipurposeSlot1 : 0
   +0x024 m_pInterfaceMap  : PlainPointer<InterfaceInfo_t *>
   +0x024 m_pMultipurposeSlot2 : 0x5ce5d68
   =7ad04c78 c_DispatchMapSlotOffsets : [0]  " $ (System.Private.CoreLib.dll"
   =7ad04c70 c_NonVirtualSlotsOffsets : [0]  " $ ($((, $ (System.Private.CoreLib.dll"
   =7ad04c60 c_ModuleOverrideOffsets : [0]  " $ ($((,$((,(,,0 $ ($((, $ (System.Private.CoreLib.dll"
   =7ad12838 c_OptionalMembersStartOffsets : [0]  "(((((((,(((,(,,0(((,(,,0(,,0,004"

從 methodtable 的布局圖來看, eax+28h 是 m_pMultipurposeSlot2 結(jié)構(gòu)的第二個字段了,因?yàn)榈谝粋€字段是 虛方法表指針,如果要驗(yàn)證的話,也很簡單,用 !dumpmt -md 05ce5d3c 把所有的方法給導(dǎo)出來,然后結(jié)合 dp 05ce5d3c 看下 0x5ce5d68 之后是不是許多的方法。

0:000> !dumpmt -md 05ce5d3c
EEClass:         05cd3380
Module:          05addb14
Name:            ConsoleApp1.Chinese
mdToken:         02000007
File:            D:\net6\ConsoleApplication2\ConsoleApp1\bin\x86\Debug\net6.0\ConsoleApp1.dll
BaseSize:        0xc
ComponentSize:   0x0
DynamicStatics:  false
ContainsPointers false
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
02610028 02605568   NONE System.Object.Finalize()
02610030 02605574   NONE System.Object.ToString()
02610038 02605580   NONE System.Object.Equals(System.Object)
02610050 026055ac   NONE System.Object.GetHashCode()
05CF1CE0 05ce5d24   NONE ConsoleApp1.Chinese.SayHello()
05CF1CE8 05ce5d30    JIT ConsoleApp1.Chinese..ctor()
0:000> dp 05ce5d3c L10
05ce5d3c  00000200 0000000c 00074088 00000005
05ce5d4c  05ce5ccc 05addb14 05ce5d7c 05cd3380
05ce5d5c  05cf1ce8 00000000 05ce5d68 02610028
05ce5d6c  02610030 02610038 02610050 05cf1ce0

仔細(xì)看輸出,上面的 05ce5d68 后面的 02610028 就是 System.Object.Finalize() 方法,02610030 對應(yīng)著 System.Object.ToString() 方法。

(4)call dword ptr [eax+10h]

有了前面的基礎(chǔ),這句話就好理解了,它是從 m_pMultipurposeSlot2 結(jié)構(gòu)中找 SayHello 所在的單元指針位置,然后做 call 調(diào)用。

0:000> !U 05cf1ce0
Unmanaged code
05cf1ce0 e88f9dde74      call    coreclr!PrecodeFixupThunk (7aadba74)
05cf1ce5 5e              pop     esi
05cf1ce6 0001            add     byte ptr [ecx],al
05cf1ce8 e913050000      jmp     05cf2200
05cf1ced 5f              pop     edi
05cf1cee 0300            add     eax,dword ptr [eax]
05cf1cf0 245d            and     al,5Dh
05cf1cf2 ce              into
05cf1cf3 0500000000      add     eax,0
05cf1cf8 0000            add     byte ptr [eax],al

從匯編看,它還是一段 樁代碼,言外之意就是該方法沒有被 JIT 編譯,如果編譯完了,這里的 05CF1CE0 05ce5d24 NONE ConsoleApp1.Chinese.SayHello()  的 Entry (05CF1CE0) 也會被同步修改,驗(yàn)證一下很簡單,我們繼續(xù) go 代碼讓其編譯完成,然后再 dumpmt 。

0:008> !dumpmt -md 05ce5d3c
EEClass:         05cd3380
Module:          05addb14
Name:            ConsoleApp1.Chinese
mdToken:         02000007
File:            D:\net6\ConsoleApplication2\ConsoleApp1\bin\x86\Debug\net6.0\ConsoleApp1.dll
BaseSize:        0xc
ComponentSize:   0x0
DynamicStatics:  false
ContainsPointers false
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
02610028 02605568   NONE System.Object.Finalize()
02610030 02605574   NONE System.Object.ToString()
02610038 02605580   NONE System.Object.Equals(System.Object)
02610050 026055ac   NONE System.Object.GetHashCode()
05CF2270 05ce5d24    JIT ConsoleApp1.Chinese.SayHello()
05CF1CE8 05ce5d30    JIT ConsoleApp1.Chinese..ctor()

0:008> dp 05ce5d3c L10
05ce5d3c  00000200 0000000c 00074088 00000005
05ce5d4c  05ce5ccc 05addb14 05ce5d7c 05cd3380
05ce5d5c  05cf1ce8 00000000 05ce5d68 02610028
05ce5d6c  02610030 02610038 02610050 05cf2270

此時可以看到它由 05cf1ce0 變成了 05cf2270, 這個就是 JIT 編譯后的方法代碼,我們用 !U 反編譯下。

0:008> !U 05cf2270
Normal JIT generated code
ConsoleApp1.Chinese.SayHello()
ilAddr is 05E720D5 pImport is 008F6E88
Begin 05CF2270, size 27

D:\net6\ConsoleApplication2\ConsoleApp1\Program.cs @ 28:
>>> 05cf2270 55              push    ebp
05cf2271 8bec            mov     ebp,esp
05cf2273 50              push    eax
05cf2274 894dfc          mov     dword ptr [ebp-4],ecx
05cf2277 833d74dcad0500  cmp     dword ptr ds:[5ADDC74h],0
05cf227e 7405            je      05cf2285
05cf2280 e8cb2bf174      call    coreclr!JIT_DbgIsJustMyCode (7ac04e50)
05cf2285 90              nop

D:\net6\ConsoleApplication2\ConsoleApp1\Program.cs @ 29:
05cf2286 8b0d74207e04    mov     ecx,dword ptr ds:[47E2074h] ("chinese")
05cf228c e8dffbffff      call    05cf1e70
05cf2291 90              nop

D:\net6\ConsoleApplication2\ConsoleApp1\Program.cs @ 30:
05cf2292 90              nop
05cf2293 8be5            mov     esp,ebp
05cf2295 5d              pop     ebp
05cf2296 c3              ret

終于這就是多態(tài)下的 ConsoleApp1.Chinese.SayHello 方法啦。

三、總結(jié)

到此這篇關(guān)于C# 中的多態(tài)底層虛方法調(diào)用詳情的文章就介紹到這了,更多相關(guān) C# 多態(tài)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#使用iTextSharp生成PDF的示例代碼

    C#使用iTextSharp生成PDF的示例代碼

    這篇文章主要為大家詳細(xì)介紹了C#如何使用iTextSharp生成PDF的相關(guān)知識,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以了解下
    2024-03-03
  • C#使用oledb操作excel文件的方法

    C#使用oledb操作excel文件的方法

    這篇文章主要介紹了C#使用oledb操作excel文件的方法,涉及C#中oledb操作excel的相關(guān)技巧,非常具有實(shí)用價值,需要的朋友可以參考下
    2015-05-05
  • 輸出的文本實(shí)現(xiàn)對齊的方法(超簡單)

    輸出的文本實(shí)現(xiàn)對齊的方法(超簡單)

    下面小編就為大家分享一篇c#輸出的文本實(shí)現(xiàn)對齊的方法,特別簡單!希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • C#之CLR內(nèi)存字符串常量池(string)

    C#之CLR內(nèi)存字符串常量池(string)

    這篇文章主要介紹了C#之CLR內(nèi)存字符串常量池(string),對于學(xué)習(xí)和理解C#內(nèi)存原理很有幫助,需要的朋友可以參考下
    2014-08-08
  • C#實(shí)現(xiàn)遠(yuǎn)程關(guān)閉計(jì)算機(jī)或重啟計(jì)算機(jī)的方法

    C#實(shí)現(xiàn)遠(yuǎn)程關(guān)閉計(jì)算機(jī)或重啟計(jì)算機(jī)的方法

    這篇文章主要介紹了C#實(shí)現(xiàn)遠(yuǎn)程關(guān)閉計(jì)算機(jī)或重啟計(jì)算機(jī)的方法,涉及C#遠(yuǎn)程連接及系統(tǒng)命令的調(diào)用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-08-08
  • 說說C#的async和await的具體用法

    說說C#的async和await的具體用法

    本篇文章主要介紹了說說C#的async和await的具體用法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • 淺談C#多線程簡單例子講解

    淺談C#多線程簡單例子講解

    本篇文章主要介紹了C#多線程簡單例子,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • C#創(chuàng)建dll類庫的圖文步驟

    C#創(chuàng)建dll類庫的圖文步驟

    類庫讓我們的代碼可復(fù)用,我們只需要在類庫中聲明變量一次,就能在接下來的過程中無數(shù)次地使用,而無需在每次使用前都要聲明它。這樣一來,就節(jié)省了我們的內(nèi)存空間,需要的朋友可以參考下
    2017-01-01
  • C#讀取QQ純真IP數(shù)據(jù)庫QQWry.Dat的代碼

    C#讀取QQ純真IP數(shù)據(jù)庫QQWry.Dat的代碼

    QQ純真IP庫算是IP地址收集較為全的一個IP庫,對于IP查詢來說這個是不錯的選擇。下面是如何查詢純真IP庫的一個類,使用C#代碼。
    2007-03-03
  • C#實(shí)現(xiàn)一個控制臺的點(diǎn)餐系統(tǒng)

    C#實(shí)現(xiàn)一個控制臺的點(diǎn)餐系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)一個控制臺的點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11

最新評論

巨野县| 辰溪县| 灌阳县| 庐江县| 阿合奇县| 乌拉特前旗| 新昌县| 武定县| 稻城县| 武义县| 哈密市| 华安县| 凤城市| 原平市| 邵东县| 通渭县| 蒲江县| 子洲县| 栾城县| 盈江县| 宜城市| 宝兴县| 滨州市| 韶关市| 扎赉特旗| 长治县| 沐川县| 广平县| 平果县| 高陵县| 轮台县| 太白县| 厦门市| 库尔勒市| 久治县| 日土县| 建湖县| 聂荣县| 清河县| 双鸭山市| 饶平县|