首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何摆脱虚拟表?密封级

如何摆脱虚拟表?密封级
EN

Stack Overflow用户
提问于 2017-02-28 10:09:07
回答 4查看 780关注 0票数 9

据我所知,使类sealed摆脱了在VTable中查找还是我错了?如果我创建一个类sealed,这是否意味着类层次结构中的所有虚拟方法都被标记为sealed

例如:

代码语言:javascript
复制
public class A {
    protected virtual void M() { ........ }
    protected virtual void O() { ........ }
}

public sealed class B : A {
    // I guess I can make this private for sealed class
    private override void M() { ........ }
    // Is this method automatically sealed? In the meaning that it doesn't have to look in VTable and can be called directly?

    // Also what about O() can it be called directly too, without VTable?
}
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-02-28 10:14:47

“我想我可以把这封封的课变成私人的”

不能在继承层次结构中更改访问修饰符。这意味着如果方法在基类中是public,则不能使其在派生类中成为privateinternalprotected。只有将方法声明为new时,才能更改修饰符。

代码语言:javascript
复制
private new void M() { ........ }

据我所知,将类密封起来,不用在VTable中查找,还是我错了?

密封类是层次结构中的最后一个类,因为您无法继承它。虚拟表可与sealed类一起使用,以防此sealed类重写基类中的某些方法。

如果我使类密封,这是否意味着类层次结构中的所有虚拟方法也被标记为密封?

您可以看到IL代码:

代码语言:javascript
复制
.method family hidebysig virtual            // method is not marked as sealed
    instance void M () cil managed 
{        
    .maxstack 8

    IL_0000: nop 
    IL_0001: ret value
}  

方法未标记为sealed。即使将此方法显式标记为sealed,也会得到相同的IL代码。

此外,没有理由在sealed类中将方法标记为sealed。如果类是sealed,那么不能继承它,也不能继承它的方法。

关于虚拟表-如果方法正在重写,并且您从虚拟表中删除它,您就永远不能在继承层次结构中使用它,因此没有理由重写方法,也没有理由在继承层次结构中使用它。

票数 2
EN

Stack Overflow用户

发布于 2017-02-28 11:34:06

首先要注意的是,C#通常对引用类型上的任何实例方法进行虚拟调用,即使该方法不是虚拟的。这是因为有一个C#规则,在一个不是.NET规则的空引用上调用一个方法是非法的(在原始CIL中,如果您在一个空引用上调用一个方法,并且该方法本身并不访问一个字段或虚拟方法,那么它工作得很好),而使用callvirt是一种执行该规则的廉价方法。

C#将为非虚拟实例调用生成call而不是callvirt,在某些情况下,很明显引用不是null。特别是对于obj?.SomeMethod(),因为?.意味着已经发生了空检查,那么如果SomeMethod()不是虚拟的,这将被编译到call。这只会在?.中发生,因为编译?.的代码可以进行检查,而if (obj != null){obj.SomeMethod();}则不会这样做,因为编译.的代码不知道它在执行空检查。所涉及的逻辑是非常本地化的。

在CIL级别,可以跳过虚拟方法的虚拟表查找。这就是base调用的工作方式;编译为call的基础是方法的实现,而不是callvirt。通过在构造obj?.SomeMethod()中的扩展,SomeMethod是虚拟的和密封的(无论是单独的还是因为obj的类型是密封的),那么理论上就有可能将其编译成一个call到大多数派生类型的实现。但是,需要进行一些额外的检查,特别是要确保声明类型和密封类型之间的层次结构中的类是否仍然正确工作,添加或删除重写。为了保证优化是安全的,需要对层次结构的一些全局知识(并确保知识不会改变,这意味着当前正在编译的程序集中的所有类型)。而且收益很小。而且大多数情况下仍然不可用,原因与使用callvirt的原因相同,即使在大多数非虚拟调用中也是如此。

我不认为sealed会影响编译器生成调用的方式,而且大多数时候它也不会影响它。

尽管抖动可以自由地应用于更多的知识,但如果有的话,两者之间的差别也会很小。我当然会推荐将类标记为sealed,如果抖动利用了这一点,那就太好了,但是我推荐它的主要原因不是性能,而是正确性。如果您在某个地方试图重写标记为sealed的类,那么A.您只是稍微更改了设计,并且知道您必须删除sealed (.5seconds工作来删除它),或者B。您在一个地方做了一些事情,您肯定不会在另一个地方做一些事情。有一个短暂的暂停重新考虑是很好的。

如果我使类密封,这是否意味着类层次结构中的所有虚拟方法也被标记为密封?

它们被认为是密封的,就像显式地标记为密封一样。

票数 2
EN

Stack Overflow用户

发布于 2017-02-28 10:18:11

密封意味着您不能从它继承或覆盖它。如果无法从类B继承,则无法重写方法M。至于查找,我认为您没有保存它。在您的示例中,调用方法O必须从A类中查找该方法并调用它。通过vtable进行的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42505773

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档