C# CLR实现对接口成员的虚拟方法调用

C# CLR实现对接口成员的虚拟方法调用,c#,interface,clr,dispatch,method-call,C#,Interface,Clr,Dispatch,Method Call,出于好奇:CLR如何将虚拟方法调用分派给接口成员以实现正确的实现 我知道CLR为每种类型维护的VTable,每个方法都有方法槽,而且对于每个接口,它都有一个额外的方法槽列表,这些方法槽指向关联的接口方法实现。但我不明白以下几点:CLR如何有效地确定从类型的VTable中选择哪个接口方法槽列表 MSDN杂志2005年5月号的一篇文章讨论了一个进程级映射表IVMap,该表由接口ID索引。这是否意味着同一进程中的所有类型都具有指向同一IVMap的相同指针 它还指出: 如果MyInterface1由两个

出于好奇:CLR如何将虚拟方法调用分派给接口成员以实现正确的实现

我知道CLR为每种类型维护的VTable,每个方法都有方法槽,而且对于每个接口,它都有一个额外的方法槽列表,这些方法槽指向关联的接口方法实现。但我不明白以下几点:CLR如何有效地确定从类型的VTable中选择哪个接口方法槽列表

MSDN杂志2005年5月号的一篇文章讨论了一个进程级映射表IVMap,该表由接口ID索引。这是否意味着同一进程中的所有类型都具有指向同一IVMap的相同指针

它还指出:

如果
MyInterface1
由两个类实现,则将有两个类 IVMap表中的条目。条目将指向后面的开始 嵌入在
MyClass
方法表中的子表的

CLR如何知道选择哪个条目?它是否进行线性搜索以查找与当前类型匹配的条目?还是二进制搜索?或者某种直接索引,并有一个可能有许多空条目的地图


我也读过关于CLR中通过C#第三版的接口的章节,但它没有提到这一点。因此,的答案并不能回答我的问题。

来自您链接的第一篇文章:

如果MyInterface1由两个类实现,那么将有两个类 IVMap表中的条目。条目将指向后面的开始 嵌入在MyClass方法表中的子表的 图9

类加载器遍历当前类的元数据, 父类和接口,并创建方法表。在 布局过程中,它替换任何重写的虚拟方法,替换 任何被隐藏的父类方法都会创建新的插槽,并 根据需要复制插槽。插槽的复制是必要的 创建每个界面都有自己的迷你vtable的错觉。 但是,复制的插槽指向相同的物理插槽 实施

这对我来说意味着接口的IVMap中有一些条目,这些条目由类名(或某些等效项)键入,指向类的vtable的一个子部分,该子部分本质上有实现该接口的每个类的方法的重复实现,由指向与类自己的vtable条目相同的物理实现的指针支持

但可能完全错了。

如果您查看链接站点上的图表,可能会更容易理解

这是否意味着同一进程中的所有类型都具有指向同一IVMap的相同指针

是的,因为它在域级别,这意味着AppDomain中的所有内容都具有相同的IVMap

CLR如何知道选择哪个条目?它是否进行线性搜索以查找与当前类型匹配的条目?还是二进制搜索?或者某种直接索引,并有一个可能有许多空条目的地图

这些类是用偏移量来布置的,所以每样东西都有一个相对固定的区域。这使得寻找方法变得更容易。它将搜索IVMap表并从接口中找到该方法。从那里,它转到MethodsTottable并使用该类的接口实现。类的接口映射保存元数据,但是,实现与任何其他方法一样处理

再次从您链接的站点:

每个接口实现在IVMap中都有一个条目。如果MyInterface1由两个类实现,那么IVMap表中将有两个条目。该条目将指向MyClass方法表中嵌入的子表的开头

这意味着每次实现一个接口时,它在IVMap中都有一个唯一的记录,该记录指向MethodsToTable,而MethodsToTable又指向实现。因此它知道根据调用它的类选择哪个实现,因为IVMap记录指向调用该方法的类中的MethodsTottable。所以我想象它只是通过IVMap进行线性搜索,找到正确的实例,然后它们就开始运行了


编辑:提供有关IVMap的更多信息

同样,从OP中的链接:

第一个InterfaceInfo入口的前4个字节指向MyInterface1的TypeHandle(见图9和图10)。下一个字(2字节)由标志占用(其中0从父级继承,1在当前类中实现)。标志后面的单词是Start Slot,类装入器使用它来布局接口实现子表

这里我们有一个表,其中数字是字节的偏移量。这只是IVMap中的一条记录:

+----------------------------------+
| 0 - InterfaceInfo                |
+----------------------------------+
| 4 - Parent                       |
+----------------------------------+
| 5 - Current Class                |
+----------------------------------+
| 6 - Start Slot (2 Bytes)         |
+----------------------------------+
假设这个AppDomain中有100条接口记录,我们需要找到每个记录的实现。我们只是比较第5个字节,看看它是否与我们当前的类匹配,如果匹配,我们跳转到第6个字节中的代码。因为每个记录都有8字节长,所以我们需要这样做:(Psuedocode)

虽然它仍然是一个线性搜索,但实际上,只要迭代数据的大小不是很大,它不会花费那么长的时间。我希望这有帮助


编辑2:

因此,在看了图并想知道为什么图中的类在IVMap中没有插槽1之后,我重新阅读了这一部分,发现:

IVMap是基于嵌入在方法表中的接口映射信息创建的。接口映射是在MethodTable布局过程中基于类的元数据创建的。一旦类型加载完成,在方法调度中只使用IVMap

因此,类的IVMap只加载特定cl
findclass :
   if (!position == class) 
      findclass adjust offset by 8 and try again
+-------------------------+
| Slot 1 - YourInterface  |
+-------------------------+
| Slot 2 - MyInterface    |
+-------------------------+
| Slot 3 - MyInterface2   |
+-------------------------+
| Slot 4 - YourInterface2 |
+-------------------------+
mov ecx,edi
mov eax, dword ptr [ecx]
mov eax, dword ptr [ecx+08h] ; slot 2
; do stuff with slot 2
mov eax, dword ptr [ecx+10h] ; slot 3
; do stuff with slot 3