在我知道如何通过C#/ CLR回忆所需的虚拟方法之前,我无法入睡。在我的书中,richter写的不多于CLR确定对象的实际类型并调用适当的方法。例如,在C ++中,多态类的每个实例都存储一个指向虚拟表的指针。但是在C#而不是指向虚拟表的指针中,实例数据存储了一些SyncBlk Index和TypeHandle。不明白TypeHandle与C#中的这个指针有什么不同。 TypeHandle的作用是什么。 例如,在C ++中我们有
class A
{
int a;
public virtual void show() {}
};
class B: public A
{
int b;
public virtual void show() {}
};
A和B类的实例如何在内存中查找,我用伪代码编写
A:
{
vtptr; // pointer to A vt
a;
}
B:
{
vtptr; // pointer to A vt + B vt
a;
b;
}
然后在C ++中我们运行代码
A* pa = new B();
pa->show();
很明显,我们创建B实例并将其转换为A类型,但我们不会丢失show()的重写地址,并且感谢我们可以调用B :: show()。 我真的需要了解C#/ CLR如何执行强制转换为基类型并定义虚方法调用的类似示例。请帮忙!我很高兴知道所有技术细节
答案 0 :(得分:2)
“TypeHandle”是一个流行的用词不当。它实际上是MethodTable指针,它在CLR源中的命名方式。托管对象的MethodTable在功能上非常类似于C ++ v表,一个指向类方法的指针表。一个小的区别是它不仅包含虚方法,该表还用于及时编译方法。
所以它只是以完全相同的方式工作,对该表中的条目进行简单的间接调用。抖动知道表中方法指针的偏移量,就像C ++编译器一样。它的运行速度与本机C ++代码一样快。
您使用C#编写的代码段并使用如下:
A obj = new B();
obj.show();
在运行时生成此32位机器代码:
00000003 mov ecx,51B034Ch ; typeref for class B
00000008 call FBB90BF4 ; call operator new
0000000d mov ecx,eax ; setup this pointer
0000000f mov eax,dword ptr [ecx] ; obtain methodtable pointer from object
00000011 call dword ptr [eax+38h] ; indirect call to show()
从偏移量(0x38)可以看出,方法表不仅包含方法指针。您可以在SSCLI20源代码中找到有关它的详细信息。
原生C ++中的代码片段并使用如下:
int main()
{
A* obj = new B;
obj->show();
return 0;
}
生成此32位机器代码:
01361010 push 0Ch ; size of B object
01361012 call operator new (136104Ch) ; call operator new
01361017 add esp,4 ; __cdecl stack cleanup
0136101A test eax,eax ; handle null pointer
0136101C je main+1Fh (136102Fh)
0136101E mov dword ptr [eax],offset B::`vftable' (1362100h) ; initialize v-table
01361024 mov edx,dword ptr [eax] ; obtain v-table pointer from object
01361026 mov ecx,eax ; setup this pointer
01361028 mov eax,dword ptr [edx] ; get method pointer
0136102A call eax ; indirect call to show()
由于C ++语言规则,构造函数调用稍微复杂一些。方法调用是相同的,只是不同的代码。在早期的奔腾处理器上效率稍高。