Created
October 7, 2025 06:06
-
-
Save sunmeat/e5de8fcae61e41b666abea064fdf0a84 to your computer and use it in GitHub Desktop.
vtable C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Text; | |
// https://pnguyen.au/posts/virtual-new-override-csharp/ | |
// https://habr.com/ru/companies/clrium/articles/344556/ !!! | |
// https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/methodtable.h | |
class Dog | |
{ | |
// в clr кожен об'єкт так само має прихований "вказівник" на таблицю в.методів (methodtable) на початку - як __vfptr у c++. | |
// ця таблиця статична для класу і містить адреси віртуальних методів (vtable слоти). | |
// якби це було явно в c#, могло б називатися __methodtableptr, аналог __vfptr. | |
public string? name; | |
public int age; | |
public Dog() | |
{ | |
// clr автоматично ініціалізує поля на null/0 | |
} | |
/* dog.ctor в il (.net 9, компілятор генерує такий байт-код): | |
.method public hidebysig specialname rtspecialname | |
instance void .ctor() cil managed | |
{ | |
.maxstack 8 | |
ldarg.0 // завантажити this (об'єкт, що створюється) на стек - це посилання на новий об'єкт, як this у c++ | |
call instance void [System.Private.CoreLib]System.Object::.ctor() // виклик базового конструктора object - clr ініціалізує об'єкт, встановлює methodtable на dog::methodtable | |
ldarg.0 // завантажити this знову | |
ldnull // завантажити null (порожній рядок) | |
stfld string Dog::name // зберегти null у поле name - поля ініціалізуються за замовчуванням | |
ldarg.0 // завантажити this | |
ldc.i4.0 // завантажити число 0 | |
stfld int32 Dog::age // зберегти 0 у поле age | |
ret // повернути - об'єкт готовий, methodtable вже встановлена clr | |
} | |
// clr loader (завантажувач) при завантаженні класу заповнює dog::methodtable слотами: наприклад, слот для guard = адреса коду dog::guard. | |
*/ | |
public virtual void Guard() | |
{ | |
Console.WriteLine("Dog::Охороняти()"); | |
} | |
public virtual void Bark() | |
{ | |
Console.WriteLine("Dog::Гавкати()"); | |
} | |
} | |
class Sheepdog : Dog | |
{ | |
// похідний клас успадковує methodtable від dog, але clr оновлює слоти для перевизначених методів (override). | |
// наприклад, слот guard тепер вказує на sheepdog::guard, а не dog::guard. | |
public int sheepdog_field; | |
// явний конструктор sheepdog - викликає базовий, потім ініціалізує свої поля. | |
public Sheepdog() : base() | |
{ | |
// логіка для похідного класу, якщо потрібно. | |
} | |
/* sheepdog.ctor в il (.net 9): | |
.method public hidebysig specialname rtspecialname | |
instance void .ctor() cil managed | |
{ | |
.maxstack 8 | |
ldarg.0 // завантажити this | |
call instance void Dog::.ctor() // виклик базового dog.ctor - ініціалізує поля dog, встановлює methodtable на dog::methodtable тимчасово | |
ldarg.0 // завантажити this | |
ldc.i4.0 // завантажити 0 | |
stfld int32 Sheepdog::sheepdog_field // ініціалізувати поле sheepdog_field | |
ldarg.0 // завантажити this востаннє | |
ldsflda valuetype [System.Private.CoreLib]System.Type Sheepdog::type_handle // завантажити адресу типу (але clr автоматично встановлює methodtable) | |
// насправді clr після виклику base перезаписує methodtable на sheepdog::methodtable з оновленими слотами для override | |
ret | |
} | |
// loader копіює pvtable від dog, але замінює слоти для перевизначених методів на sheepdog версії. | |
*/ | |
public override void Guard() | |
{ | |
Console.WriteLine("Sheepdog::Охороняти()"); | |
} | |
public override void Bark() | |
{ | |
Console.WriteLine("Sheepdog::Гавкати()"); | |
} | |
} | |
class Program | |
{ | |
static void Main() | |
{ | |
Console.OutputEncoding = Encoding.UTF8; | |
// створення sheepdog m в il (.net 9): | |
// newobj instance void Sheepdog::.ctor() // clr: виділити пам'ять за розміром sheepdog, викликати ctor (встановити methodtable на sheepdog::) | |
// stloc.0 // зберегти в змінну m | |
Sheepdog m = new Sheepdog(); | |
// створення dog d: | |
// newobj instance void Dog::.ctor() // виділити, викликати ctor (встановити dog::methodtable) | |
Dog d = new Dog(); // clr: виділити пам'ять, встановити methodtable для dog | |
// виклик d.guard() в il (.net 9): | |
// ldloc.1 // завантажити d (this) на стек | |
// callvirt instance void Dog::Guard() // віртуальний виклик: clr шукає в methodtable слот guard і викликає відповідний метод | |
// callvirt - це динамічний диспетч, як (*(this->__vfptr[0]))() у c++, але через слоти в таблиці класу | |
d.Guard(); // callvirt instance void dog::guard() - il | |
// як clr виконує callvirt (приклад jit на x64, .net 9): | |
// this (rcx) перевіряється на null | |
// mov rax, [rcx] ; завантажити methodtable з offset 0 об'єкта (як __vfptr) | |
// mov rax, [rax + offset_слоту_guard] ; завантажити адресу методу з vtable (наприклад, offset 0x48 у methodtable) | |
// call rax ; виклик методу за адресою | |
// для non-virtual - прямий виклик, але virtual з override - динамічний пошук | |
// аналогічно для bark: | |
// ldloc.1 | |
// callvirt instance void Dog::Bark() | |
d.Bark(); // аналогічно для слоту bark | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment