如果你看一下链接站点上的图表,它可能会更容易理解。
这是不是意味着同一进程中的所有类型都具有指向相同IVMap的指针?
是的,因为它在域级别上,这意味着该AppDomain中的所有内容都具有相同的IVMap。
CLR如何知道要选择哪个条目?它是否执行线性搜索以查找与当前类型匹配的条目?还是二进制搜索?或者进行某种直接索引并具有其中可能有许多空条目的映射?
类是通过偏移量布局的,因此每个东西都具有相对固定的区域。当查找方法时,这使事情变得更容易。它将搜索IVMap表,并从接口中查找该方法。从那里,它转到MethodSlotTable并使用该类对接口的实现。该类的接口映射保存元数据,但是实现与任何其他方法一样处理。
再次来自你链接的网站:
每个接口实现都将在IVMap中具有一个条目。如果MyInterface1由两个类实现,则IVMap表中将有两个条目。该条目将指向嵌入在MyClass方法表中的子表的开头。
这意味着每次实现接口时,它在IVMap中都有唯一记录,该记录指向MethodSlotTable,后者又指向实现。因此,根据调用它的类,它知道要选择哪个实现,因为IVMap记录指向调用该方法的类中的MethodSlotTable。因此,我想它只是通过IVMap进行线性搜索以找到正确的实例,然后就可以运行了。
编辑:提供有关IVMap的更多信息。
同样来自OP中的链接:
第一个InterfaceInfo条目的前4个字节指向MyInterface1的TypeHandle(参见图9和图10)。下一个字(2个字节)由Flags占用(其中0从父项继承,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:
所以,在查看图表并想知道为什么类图中没有Slot 1在IVMap中之后,我重新阅读了该部分并找到了以下内容:
IVMap是根据方法表中嵌入的接口映射信息创建的。接口映射是在MethodTable布局过程中基于类的元数据创建的。一旦类型加载完成,只使用IVMap进行方法调度。
因此,类的IVMap仅加载其继承的接口。它似乎从域IVMap复制,但仅保留指向的接口。这引出了另一个问题,如何?很可能它与C++的vtable相同,其中每个条目都具有偏移量,并且接口映射提供要包括在IVMap中的偏移量列表。
如果我们看一下可能适用于整个域的IVMap:
+
| Slot 1 - YourInterface |
+
| Slot 2 - MyInterface |
+
| Slot 3 - MyInterface2 |
+
| Slot 4 - YourInterface2 |
+
假设在此领域中只有4个Interface Map的实现。每个插槽都会有一个偏移量(类似于我之前发布的IVMap记录),并且该类的IVMap将使用这些偏移量来访问IVMap中的记录。
假设每个插槽为8字节,插槽1从0开始,因此如果我们想要得到插槽2和3,我们可以这样做:
mov ecx,edi
mov eax, dword ptr [ecx]
mov eax, dword ptr [ecx+08h]
mov eax, dword ptr [ecx+10h]
请原谅我的x86知识不是很熟悉,但我尝试复制了与链接文章中相同的内容。