如果您查看链接站点上的图表,它可能会更容易理解。
这是否意味着同一进程中的所有类型都具有指向同一 IVMap 的相同指针?
是的,因为它在域级别,这意味着该 AppDomain 中的所有内容都具有相同的 IVMap。
CLR 如何知道选择哪个条目?它是否进行线性搜索以找到与当前类型匹配的条目?还是二分查找?还是某种直接索引并有一个可能包含许多空条目的地图?
这些类是用偏移量布局的,所以所有东西都有一个相对固定的区域。在寻找方法时,这使事情变得更容易。它将搜索 IVMap 表并从界面中找到该方法。从那里,它进入 MethodSlotTable 并使用该类的接口实现。类的接口映射包含元数据,但是,实现的处理方式与任何其他方法一样。
再次从您链接的网站:
每个接口实现都会在 IVMap 中有一个条目。如果 MyInterface1 由两个类实现,IVMap 表中将有两个条目。该条目将指向嵌入在 MyClass 方法表中的子表的开头
这意味着每次实现接口时,它在 IVMap 中都有一个唯一记录,该记录指向 MethodSlotTable,而 MethodSlotTable 又指向实现。因此,它知道根据调用它的类选择哪个实现,因为 IVMap 记录指向调用该方法的类中的 MethodSlotTable。所以我想这只是通过 IVMap 进行线性搜索以找到正确的实例,然后它们就会关闭并运行。
编辑:提供有关 IVMap 的更多信息。
同样,来自 OP 中的链接:
第一个 InterfaceInfo 条目的前 4 个字节指向 MyInterface1 的 TypeHandle(参见图 9 和图 10)。下一个 WORD(2 个字节)由 Flags 占用(其中 0 是从 parent 继承的,1 在当前类中实现)。Flags 后面的 WORD 是 Start Slot,类加载器使用它来布局接口实现子表。
所以这里我们有一个表格,其中数字是字节的偏移量。这只是 IVMap 中的一条记录:
+----------------------------------+
| 0 - InterfaceInfo |
+----------------------------------+
| 4 - Parent |
+----------------------------------+
| 5 - Current Class |
+----------------------------------+
| 6 - Start Slot (2 Bytes) |
+----------------------------------+
假设这个 AppDomain 中有 100 条接口记录,我们需要找到每一条的实现。我们只是比较第 5 个字节,看看它是否与我们当前的类匹配,如果匹配,我们跳转到第 6 个字节中的代码。因为,每条记录是 8 字节长,我们需要做这样的事情:(伪代码)
findclass :
if (!position == class)
findclass adjust offset by 8 and try again
虽然它仍然是线性搜索,但实际上,只要迭代的数据量不是很大,它不会花费那么长时间。我希望这会有所帮助。
编辑2:
因此,在查看图表并想知道为什么图表中的类的 IVMap 中没有 Slot 1 之后,我重新阅读了该部分并发现了这一点:
IVMap 是根据嵌入在方法表中的接口映射信息创建的。Interface Map 是在 MethodTable 布局过程中基于类的元数据创建的。一旦类型加载完成,在方法分派中只使用 IVMap。
因此,类的 IVMap 仅加载特定类继承的接口。它看起来像是从域 IVMap 复制的,但只保留指向的接口。这就引出了另一个问题,怎么做?很可能它相当于 C++ 执行 vtables 的方式,其中每个条目都有一个偏移量,并且接口映射提供了要包含在 IVMap 中的偏移量列表。
如果我们查看可能适用于整个域的 IVMap:
+-------------------------+
| Slot 1 - YourInterface |
+-------------------------+
| Slot 2 - MyInterface |
+-------------------------+
| Slot 3 - MyInterface2 |
+-------------------------+
| Slot 4 - YourInterface2 |
+-------------------------+
假设该域中只有 4 个接口映射的实现。每个槽都有一个偏移量(类似于我之前发布的 IVMap 记录),并且此类的 IVMap 将使用这些偏移量来访问 IVMap 中的记录。
假设每个插槽是 8 个字节,插槽 1 从 0 开始,所以如果我们想要获取插槽 2 和 3,我们可以这样做:
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
请原谅我的 x86,因为我不太熟悉它,但我试图复制他们在链接到的文章中的内容。