[英]How to get rid of virtual table? Sealed class
据我所知,制作类sealed
摆脱了VTable中的查找或我错了吗? 如果我使类sealed
,这是否意味着类层次结构中的所有虚方法也标记为sealed
?
例如:
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?
}
“我想我可以将它作为密封课程的私人”
您无法更改继承层次结构中的访问修饰符。 这意味着如果方法在基类中是public
,则不能将其设置为private
或internal
或在派生类中protected
。 只有在将方法声明为new
方法时,才能更改修改器:
private new void M() { ........ }
据我所知,制作类密封摆脱了VTable中的查找或我错了吗?
密封类是层次结构中的最后一个,因为您无法继承它。 虚拟表可以与sealed
类一起使用,以防sealed
类覆盖基类中的某些方法。
如果我使类密封,这是否意味着类层次结构中的所有虚方法也标记为密封?
你可以看到IL代码:
.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
你不能继承它,所以,你不能继承它的方法。
关于虚拟表 - 如果方法覆盖并且您从虚拟表中删除它,则永远不能在继承层次结构中使用它,因此,没有理由覆盖方法并且永远不会在继承层次结构中使用它。
首先要注意的是,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
(.5秒工作以删除它)或B.你已经在一个地方做了一件事,你确定你不会在另一个地方。 暂时重新考虑是件好事。
如果我使类密封,这是否意味着类层次结构中的所有虚方法也标记为密封?
它们被认为是密封的,就像明确标记为密封一样。
密封意味着您无法继承或覆盖它。 如果你不能从B类继承,你就无法覆盖方法M.至于查找我不认为你保存它。 在你的示例中,调用方法O必须从类A中查找方法并调用它。 那是通过vtable进行的。
密封类或方法将无效,对BM()
任何调用都将是虚拟的。
如果我不得不打赌为什么会这样,我会说它因为M()
在A
声明,覆盖它不会使该方法“属于” B
如果检查生成的代码的IL
,您将看到相关指令是callvirt instance string namespace.A::M()
,因此,即使B
被密封,调用也必须是虚拟的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.