日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CLR运行时细节 - 继承多态的实现

發(fā)布時間:2023/12/4 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CLR运行时细节 - 继承多态的实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

關于多態(tài)不多解釋了,在運行時決定和調用具體的實現,是面向對象的基礎 設計模式的基礎.
準備把繼承多態(tài)和接口多態(tài)分開,因為從CLR實現的角度繼承多態(tài)相比于接口多態(tài)要簡單得多,也更容易理解,本篇只討論繼承多態(tài), .NET Framework 2.0 和 4.0 這兩個版本在實現上稍微有點區(qū)別(這里先忽略方法Jit編譯的過程,只關注實現的方式).

廢話不多,先看代碼: C# Polymorphism01.cs



using System;

using System.Runtime.CompilerServices;

public class Program

{

public static void Main(string[] args)

{

Console.WriteLine("Polymorphism01 demo");

BaseClass bc = new BaseClass();

BaseClass bc1 = new ChlidClass();

BaseClass bc2 = new BrotherClass();

BaseClass bc3 = new DerivedOfBrotherClass();

BrotherClass bc4 = new DerivedOfBrotherClass();

bc.VirtualFun1();

bc1.VirtualFun1();

bc2.VirtualFun1();

bc3.VirtualFun1();

bc3.VirtualFun2();

bc4.VirtualFun3();

Console.ReadLine();

}

}

public class BaseClass

{

[MethodImpl(MethodImplOptions.NoInlining)]

public virtual void VirtualFun1()

{

Console.WriteLine("BaseClass VirtualFun1");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public virtual void VirtualFun2()

{

Console.WriteLine("BaseClass VirtualFun2");

}

}

public class ChlidClass : BaseClass

{

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun1()

{

Console.WriteLine("ChlidClass VirtualFun1");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun2()

{

Console.WriteLine("ChlidClass VirtualFun2");

}

}

public class BrotherClass : BaseClass

{

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun1()

{

Console.WriteLine("BrotherClass VirtualFun1");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun2()

{

Console.WriteLine("BrotherClass VirtualFun2");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public virtual void VirtualFun3()

{

Console.WriteLine("BrotherClass VirtualFun3");

}

}

public class DerivedOfBrotherClass : BrotherClass

{

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun1()

{

Console.WriteLine("DerivedOfBrotherClass VirtualFun1");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun2()

{

Console.WriteLine("DerivedOfBrotherClass VirtualFun2");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun3()

{

Console.WriteLine("DerivedOfBrotherClass VirtualFun3");

}

}


  • 編譯代碼 先用 .Net Framework 2.0 編譯:

    1

    2

    %windir%\Microsoft.NET\Framework\v2.0.50727\csc.exe /debug /target:exe /out:e:\temp\Polymorphism01_2.0.exe e:\temp\Polymorphism01.cs

    pause

  • 運行 Polymorphism01_2.0.exe

  • 啟動windbg 附加進程 加載SOS

  • 查找對應的模塊:
    !Name2EE *!Polymorphism01_2.0.exe



0:004> !Name2EE *!Polymorphism01_2.0.exe

Module: 790c1000 (mscorlib.dll)

--------------------------------------

Module: 00af2c5c (Polymorphism01_2.0.exe)


根據模塊查找方法表:
!DumpModule -mt 00af2c5c

  • 先分別看下 BaseClass BrotherClass DerivedOfBrotherClass 這3個繼承關系類的方法表(MethodTable)

可以看到第一個虛方法(ToString)的入口都是在方法表偏移28h的位置,其順序是先父類,再子類,這樣的安排讓所有同一個家族(繼承關系)的類型繼承虛方法的順序是一樣的,并且偏移量是一樣的,所有的類型(除了接口類型)的父類都是(或者間接是)System.Object,所以前4個虛方法肯定是Object里的4個虛方法(ToString Equals GetHashCode Finalize)

通過Program 的方法表(MethodTable)找到Main方法的入口地址:
!DumpMT -md 00af302c


0:004> !DumpMT -md 00af302c

EEClass: 00af12f4

Module: 00af2c5c

Name: Program

mdToken: 02000002 ?(E:\temp\Polymorphism01_2.0.exe)

BaseSize: 0xc

ComponentSize: 0x0

Number of IFaces in IFaceMap: 0

Slots in VTable: 6

--------------------------------------

MethodDesc Table

Entry MethodDesc ? ? ?JIT Name

79286aa0 ? 79104960 ? PreJIT System.Object.ToString()

79286ac0 ? 79104968 ? PreJIT System.Object.Equals(System.Object)

79286b30 ? 79104998 ? PreJIT System.Object.GetHashCode()

792f76d0 ? 791049bc ? PreJIT System.Object.Finalize()

00afc015 ? 00af3024 ? ? NONE Program..ctor()

01010070 ? 00af3018 ? ? ?JIT Program.Main(System.String[])

Main方法已經Jit編譯,看看被編譯成啥樣子:
!u 01010070

  • 這里最重要的幾行:


    01010139 8b4df8 ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-8] ? // 這里是BaseClass實例對象的地址 放到 ecx寄存器,Jit采用類似fastcall的調用協定,前2個不大于4字節(jié)的參數用 ecx edx來傳遞,而實例方法的調用第一個參數是隱含的this指針(托管對象在托管堆上的地址),如果是靜態(tài)方法就不需要傳this pointer了

    0101013c 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// 托管堆上的對象(值類型裝箱后也是一樣)第一個4字節(jié)(64位8字節(jié))是對象的方法表地址(MethodTable),這里是把方法表(MethodTable)地址賦給eax寄存器

    0101013e ff5038 ? ? ? ? ?call ? ?dword ptr [eax+38h] ? ?// 這里就是實際的方法調用 上面說了 第一個虛方法在方法表的偏移28h位置,前4個是Object里的4個虛方法,所以 VirtualFun1 的入口在方法表地址(MT) + 28h + 4×4字節(jié) 也就是偏移38h的位置

    01010141 90 ? ? ? ? ? ? ?nop

    01010142 8b4df4 ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-0Ch] ? ?// 這里是 ChlidClass的對象地址賦給ecx

    01010145 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// 同樣ChlidClass的方法表地址賦給eax

    01010147 ff5038 ? ? ? ? ?call ? ?dword ptr [eax+38h] ? ?// 調用ChlidClass方法表偏移38h的方法,也是VirtualFun1 方法

    0101014a 90 ? ? ? ? ? ? ?nop

    0101014b 8b4df0 ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-10h] ? ?// BrotherClass的對象地址賦給ecx

    0101014e 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// BrotherClass方法表地址賦給eax

    01010150 ff5038 ? ? ? ? ?call ? ?dword ptr [eax+38h] ? ?// 調用BrotherClass方法表偏移38h的方法,也是VirtualFun1 方法

    01010153 90 ? ? ? ? ? ? ?nop

    01010154 8b4dec ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-14h] ? ?// DerivedOfBrotherClass的對象地址賦給ecx

    01010157 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// DerivedOfBrotherClass方法表地址賦給eax

    01010159 ff5038 ? ? ? ? ?call ? ?dword ptr [eax+38h] ? ? // 調用DerivedOfBrotherClass方法表偏移38h的方法,也是VirtualFun1 方法

    0101015c 90 ? ? ? ? ? ? ?nop

    0101015d 8b4dec ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-14h] ? ?// 還是DerivedOfBrotherClass對象地址

    01010160 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// DerivedOfBrotherClass的方法表賦給eax

    01010162 ff503c ? ? ? ? ?call ? ?dword ptr [eax+3Ch] ? ?// 這次偏移不一樣了,第6個方法 VirtualFun2 (28h+5×4字節(jié))

    01010165 90 ? ? ? ? ? ? ?nop

    01010166 8b4de8 ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-18h] ? ?// 還是DerivedOfBrotherClass對象地址

    01010169 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// DerivedOfBrotherClass的方法表賦給eax

    0101016b ff5040 ? ? ? ? ?call ? ?dword ptr [eax+40h] ? ?// 這次偏移又不一樣了,第7個方法 VirtualFun3 (28h+6×4字節(jié))

  • 可以看到 繼承多態(tài)在CLR運行時的實現是通過方法表的偏移 間接調用的,而方法表內繼承虛方法的構建順序是先父類再子類,由于.NET是單一繼承,這樣就確保了在同一家族的同一虛方法的偏移量是一樣的.

  • 接下來用Framework 4.0 編譯下源碼,4.0 和2.0相比 在實現上多了一層間接尋址,但思路是一樣的



%windir%\Microsoft.NET\Framework\v4.0.30319\csc.exe /debug /target:exe /out:e:\temp\Polymorphism01_4.0.exe e:\temp\Polymorphism01.cs

pause


  • 運行 Polymorphism01_4.0.exe

  • 啟動windbg 附加進程 加載SOS (這里要加載對于4.0的sos.dll)

  • 直接查找Main方法:
    !Name2EE Polymorphism01_4.0.exe Program.Main



0:004> !Name2EE Polymorphism01_4.0.exe Program.Main

Module: ? ? ?00b32ea4

Assembly: ? ?Polymorphism01_4.0.exe

Token: ? ? ? 06000001

MethodDesc: ?00b33838

Name: ? ? ? ?Program.Main(System.String[])

JITTED Code Address: 033a0070


  • 看Main方法的區(qū)別:!u 033a0070
    這里只截取最重要的一段,調用構造器和其他的部分都先忽略


    ...

    e:\temp\Polymorphism01.cs @ 16:

    033a0139 8b4df8 ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-8] ?// 這個還是一樣BaseClass對象的地址賦給ecx

    033a013c 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// 還是對象的第一個4字節(jié)是方法表地址 賦給eax

    033a013e 8b4028 ? ? ? ? ?mov ? ? eax,dword ptr [eax+28h] ? ?// 這里是和2.0的區(qū)別 所有繼承的虛方法的起始地址保存在方法表偏移28h的位置,也就是偏移量不是從方法表地址開始算了

    033a0141 ff5010 ? ? ? ? ?call ? ?dword ptr [eax+10h] ? ?// 這里的方式一樣的 eax是虛方法的起始位置了,前4個是Object的4個虛方法,偏移10h是第5個方法 VirtualFun1

    033a0144 90 ? ? ? ? ? ? ?nop

    e:\temp\Polymorphism01.cs @ 17:

    033a0145 8b4df4 ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-0Ch] ? ?// ChlidClass對象地址賦給ecx

    033a0148 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// ChlidClass方法表地址賦給eax

    033a014a 8b4028 ? ? ? ? ?mov ? ? eax,dword ptr [eax+28h] ? ?// 虛表入口地址賦給eax

    033a014d ff5010 ? ? ? ? ?call ? ?dword ptr [eax+10h] ? ?//還是偏移到第5個方法 VirtualFun1

    033a0150 90 ? ? ? ? ? ? ?nop

    e:\temp\Polymorphism01.cs @ 18:

    033a0151 8b4df0 ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-10h] ? ?// BrotherClass對象地址賦給ecx

    033a0154 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ? // BrotherClass方法表地址賦給eax

    033a0156 8b4028 ? ? ? ? ?mov ? ? eax,dword ptr [eax+28h] ? ?// 虛表入口地址賦給eax

    033a0159 ff5010 ? ? ? ? ?call ? ?dword ptr [eax+10h] ? ?//還是偏移到第5個方法 VirtualFun1

    033a015c 90 ? ? ? ? ? ? ?nop

    e:\temp\Polymorphism01.cs @ 19:

    033a015d 8b4dec ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-14h] ? ?// DerivedOfBrotherClass對象地址賦給ecx

    033a0160 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx] ? ?// DerivedOfBrotherClass方法表地址賦給eax

    033a0162 8b4028 ? ? ? ? ?mov ? ? eax,dword ptr [eax+28h] ? ?// 虛表入口地址賦給eax

    033a0165 ff5010 ? ? ? ? ?call ? ?dword ptr [eax+10h] ? ?//還是偏移到第5個方法 VirtualFun1

    033a0168 90 ? ? ? ? ? ? ?nop

    e:\temp\Polymorphism01.cs @ 20:

    033a0169 8b4dec ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-14h] ? ?// 上面同一個對象

    033a016c 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx]

    033a016e 8b4028 ? ? ? ? ?mov ? ? eax,dword ptr [eax+28h]

    033a0171 ff5014 ? ? ? ? ?call ? ?dword ptr [eax+14h] ? ?// 這里比上面的調用多偏移了4個字節(jié) 也就是第6個方法 VirtualFun2

    033a0174 90 ? ? ? ? ? ? ?nop

    e:\temp\Polymorphism01.cs @ 21:

    033a0175 8b4de8 ? ? ? ? ?mov ? ? ecx,dword ptr [ebp-18h] ? ?// 和上面不是同一個對象地址,但是是實例化同樣類型的對象

    033a0178 8b01 ? ? ? ? ? ?mov ? ? eax,dword ptr [ecx]

    033a017a 8b4028 ? ? ? ? ?mov ? ? eax,dword ptr [eax+28h]

    033a017d ff5018 ? ? ? ? ?call ? ?dword ptr [eax+18h] ? ? // 這里比上面的調用再多偏移了4個字節(jié) 也就是第7個方法 VirtualFun3

    033a0180 90 ? ? ? ? ? ? ?nop

    ...

  • .NET 4.0 比2.0 多了一次間接尋址,就是先偏移到虛表的入口,再從這個入口開始偏移到相應的方法,這樣的好處(個人覺得)虛表的存儲位置可以更靈活 如果方法表(MT)包含多個可變長結構也沒問題 只要入口地址保存在偏移28h的位置即可

參考文檔:

https://www.microsoft.com/china/MSDN/library/netFramework/netframework/JITCompiler.mspx?mfr=true
http://www.codeproject.com/Articles/20481/NET-Type-Internals-From-a-Microsoft-CLR-Perspecti
http://blogs.microsoft.co.il/sasha/2012/03/15/virtual-method-dispatch-and-object-layout-changes-in-clr-40/
http://www.cnblogs.com/BlueTzar/articles/884694.html

相關文章:

  • CLR運行時細節(jié) - Method Descriptor

原文地址:https://espider.github.io/CLR/inheritance-polymorphism/


.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

贊賞

人贊賞

總結

以上是生活随笔為你收集整理的CLR运行时细节 - 继承多态的实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。