【问题标题】:How does the callvirt .NET instruction work for interfaces?callvirt .NET 指令如何用于接口?
【发布时间】:2013-09-25 19:54:25
【问题描述】:

向某人解释虚拟调度很容易:每个对象都有一个指向表的指针作为其数据的一部分。类上有 N 个虚方法。对特定方法的每次调用都会在对象到达时索引对象并调用表中的第 i 个方法。每个实现方法 X() 的类都将在相同的第 i 个索引中包含方法 X() 的代码。

然后我们得到接口。并且接口需要某种形式的扭曲,因为实现相同接口的两个非继承类将在表的不同索引中具有虚函数。

我在网上搜索了很多关于如何实现接口调度的讨论。有两大类: a) 某种哈希表在对象上查找以找到正确的调度表 b) 当对象被强制转换为接口时,会创建一个新指针,该指针指向相同的数据,但指向不同的 vtable。

但是,尽管有很多关于它如何 工作的信息,但我找不到任何关于 .NET 运行时引擎如何实际实现它的信息。

有没有人知道描述当对象类型是接口时在 callvirt 指令中发生的实际指针运算的文档?

【问题讨论】:

  • 颤抖吧,凡人……全能的飞碟接近了。
  • 为了增加更多的魔力,单一类型可以实现任意数量的接口(例如,给定interface ISelf<T> {T GetMe();},像class bob : ISelf<bob> 这样的类不仅可以实现@987654323 @,还有 ISelf<ISelf<bob>>ISelf<ISelf<ISelf<bob>>> 等到任何所需的嵌套级别,并且与每个接口关联的 GetMe() 方法将“知道”它的返回类型。
  • @supercat 我从来没有想过这个问题。那是……出乎意料。这意味着通过泛型,即使没有动态加载,类型表也有些动态。我得看看那个。谢谢。

标签: c# .net


【解决方案1】:

CLR 中的接口调度是黑魔法。

正如您正确指出的那样,虚拟方法分派在概念上很容易解释。事实上,我在本系列文章中这样做了,我在其中描述了如何用一种缺乏虚拟方法的类 C# 语言实现虚拟方法:

http://blogs.msdn.com/b/ericlippert/archive/2011/03/17/implementing-the-virtual-method-pattern-in-c-part-one.aspx

我描述的机制与实际使用的机制非常相似。

接口调度更难描述,CLR 实现它的方式一点也不明显。接口调度的 CLR 机制经过仔细调整,可为最常见的情况提供高性能,因此这些机制的细节可能会随着 CLR 团队对实际使用模式的了解越来越多而发生变化。

基本上,它在幕后工作的方式是每个调用站点——即代码中调用接口方法的每个点——都有一个小缓存,上面写着“我认为与这个接口相关联的方法插槽是……在这里”。绝大多数情况下,缓存是正确的;您很少使用一百万种不同的实现调用相同的接口方法一百万次。它通常是一遍又一遍地重复相同的实现,连续多次。

如果缓存结果是未命中,则它会回退到维护的哈希表,以进行稍慢的查找。

如果结果是未命中,则分析对象元数据以确定接口槽对应的方法。

最终效果是,在给定的调用站点,如果您始终调用映射到特定类方法的接口方法,它非常快。如果您总是为给定的接口方法调用少数类方法之一,那么性能非常好。最糟糕的做法是永远不要在同一个站点用同一个接口方法调用同一个类方法两次;每次都走最慢的路径。

如果您想知道如何在内存中维护慢速查找的表,请参阅 Matthew Watson 的答案中的链接。

【讨论】:

  • 我看了Mathew提到的文章,但还是不知道Interface VTable Map里面的搜索是怎么实现的?另外,通过 AppDomain-wide Interface VTable Map 而不是单独的 Interface maps 进行调度的原因是什么?
【解决方案2】:

因为编译器总是必须有一个实际的对象来调用方法(在运行时),它总是在运行时知道它正在处理的具体类型。

调用虚方法的代码首先确定了所使用对象的类型。然后它查询类型的方法表以查找被调用的方法。然后代码简单地调用该方法,将对象的引用作为“this”与任何其他参数一起传递。

我怀疑您感兴趣的关键部分是代码如何在类型的方法表中查找方法的地址。

有关方法表的更多详细信息,请参见 2005 年 5 月版 MSDN 杂志的“JIT and Run”文章(在撰写本文时,可以从 this page 下载“.chm”文件 - 但您由于安全限制,必须转到文件的属性将其解锁,然后才能正确显示。)

关于查找是如何完成的仍然有点手忙脚乱,但它确实提供了很多其他细节。

【讨论】:

  • 不幸的是,链接似乎失效了。
猜你喜欢
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-06
  • 2021-04-14
  • 2012-07-06
  • 1970-01-01
相关资源
最近更新 更多