如何摆脱虚拟table?密封 class
How to get rid of virtual table? Sealed class
据我所知,使 class sealed
摆脱了在 VTable 中查找,还是我错了?如果我创建 class sealed
这是否意味着 class 层次结构中的所有虚拟方法也被标记为 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?
}
"I guess I can make this private for sealed class"
您不能更改继承层次结构中的访问修饰符。这意味着如果方法在基础 class 中是 public
,则不能在派生的 class 中使它成为 private
或 internal
或 protected
。仅当将方法声明为 new
:
时才能更改修饰符
private new void M() { ........ }
As far as I know making class sealed gets rid of look up in VTable or
am I wrong?
Sealed class 在层次结构中排在最后,因为您不能从它继承。虚拟 table 可以与 sealed
class 一起使用,以防此 sealed
class 覆盖基础 class 中的某些方法。
If I make a class sealed does this mean that all virtual methods in
class hierarchy are also marked 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
class 中将方法标记为sealed
。如果 class 是 sealed
你不能继承它,你不能继承它的方法。
关于 virtual tables - 如果 method 被覆盖并且你从 virtual table 中删除它你永远不能在继承层次结构中使用它所以,没有理由覆盖方法并且永远不要使用它在继承层次结构中。
Sealed 意味着您无法继承或覆盖它。如果你不能从 class B 继承,你就没有办法覆盖方法 M。
至于查找,我认为您不会保存它。在您的示例中,调用方法 O 必须从 class A 中查找方法并调用它。这是通过 vtable 发生的。
使 class 或方法密封将无效,对 B.M()
的任何调用都将是虚拟的。
如果我不得不打赌为什么会这样,我会说是因为 M()
是在 A
中声明的,覆盖它不会使方法 "belong" 变为 B
.
如果你检查生成代码的IL
,你会看到相关指令是callvirt instance string namespace.A::M()
,因此,即使B
被密封,调用也必须是虚拟。
首先要注意的是,C# 通常对引用类型的任何实例方法进行虚拟调用,即使该方法不是虚拟的。这是因为有一条 C# 规则规定,在非 .NET 规则的空引用上调用方法是非法的(在原始 CIL 中,如果您在空引用上调用方法并且该方法本身不访问字段或虚拟方法,它工作正常)并且使用 callvirt
是执行该规则的一种廉价方式。
C# 将为非虚拟实例调用生成 call
而不是 callvirt
在某些情况下,引用显然不为空。特别是 obj?.SomeMethod()
,因为 ?.
意味着空检查已经发生,那么如果 SomeMethod()
不是虚拟的,它将被编译为 call
。这只发生在 ?.
中,因为编译 ?.
的代码可以进行该检查,而 if (obj != null){obj.SomeMethod();}
不会发生,因为编译 .
的代码不知道它是在空检查之后。 涉及的逻辑非常本地化。
可以在 CIL 级别跳过对虚拟方法的虚拟 table 查找。这就是 base
呼叫的工作方式;编译为 call
上的方法实现,而不是 callvirt
。通过在构造 obj?.SomeMethod()
中扩展,其中 SomeMethod
是虚拟的和密封的(无论是单独的还是因为 obj
的类型被密封)然后理论上可以将其编译为 call
到最派生类型的实现。尽管在声明类型和密封类型之间的层次结构中添加或删除重写 classes 时,需要特别进行一些额外的检查以确保它仍然正常工作。它需要对层次结构有一些全局了解(并确保知识不会改变,这意味着当前正在编译的程序集中的所有类型)才能使优化安全。而且收益很小。而且大部分时间仍然不可用,原因与 callvirt
在大多数时间甚至在非虚拟呼叫中使用的原因相同。
我认为 sealed
不会在任何地方影响编译器生成调用的方式,而且大多数时候肯定不会影响它。
虽然抖动可以自由应用更多知识,但如果有的话,差异也会非常小。我当然建议将 classes 标记为 sealed
,如果抖动利用了它,那很好,但我推荐它的主要原因不是性能而是正确性。如果你在某个地方试图覆盖你标记为 sealed
的 class 那么要么 A. 你只是稍微改变了设计并且知道你必须删除 sealed
(. 5seconds work to remove it) or B. 你在一个地方做了一些你确信你不会在另一个地方做的事情。重新考虑的短暂停顿很好。
If I make a class sealed does this mean that all virtual methods in class hierarchy are also marked sealed?
它们被视为密封,就像明确标记为密封一样。
据我所知,使 class sealed
摆脱了在 VTable 中查找,还是我错了?如果我创建 class sealed
这是否意味着 class 层次结构中的所有虚拟方法也被标记为 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?
}
"I guess I can make this private for sealed class"
您不能更改继承层次结构中的访问修饰符。这意味着如果方法在基础 class 中是 public
,则不能在派生的 class 中使它成为 private
或 internal
或 protected
。仅当将方法声明为 new
:
private new void M() { ........ }
As far as I know making class sealed gets rid of look up in VTable or am I wrong?
Sealed class 在层次结构中排在最后,因为您不能从它继承。虚拟 table 可以与 sealed
class 一起使用,以防此 sealed
class 覆盖基础 class 中的某些方法。
If I make a class sealed does this mean that all virtual methods in class hierarchy are also marked 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
class 中将方法标记为sealed
。如果 class 是 sealed
你不能继承它,你不能继承它的方法。
关于 virtual tables - 如果 method 被覆盖并且你从 virtual table 中删除它你永远不能在继承层次结构中使用它所以,没有理由覆盖方法并且永远不要使用它在继承层次结构中。
Sealed 意味着您无法继承或覆盖它。如果你不能从 class B 继承,你就没有办法覆盖方法 M。 至于查找,我认为您不会保存它。在您的示例中,调用方法 O 必须从 class A 中查找方法并调用它。这是通过 vtable 发生的。
使 class 或方法密封将无效,对 B.M()
的任何调用都将是虚拟的。
如果我不得不打赌为什么会这样,我会说是因为 M()
是在 A
中声明的,覆盖它不会使方法 "belong" 变为 B
.
如果你检查生成代码的IL
,你会看到相关指令是callvirt instance string namespace.A::M()
,因此,即使B
被密封,调用也必须是虚拟。
首先要注意的是,C# 通常对引用类型的任何实例方法进行虚拟调用,即使该方法不是虚拟的。这是因为有一条 C# 规则规定,在非 .NET 规则的空引用上调用方法是非法的(在原始 CIL 中,如果您在空引用上调用方法并且该方法本身不访问字段或虚拟方法,它工作正常)并且使用 callvirt
是执行该规则的一种廉价方式。
C# 将为非虚拟实例调用生成 call
而不是 callvirt
在某些情况下,引用显然不为空。特别是 obj?.SomeMethod()
,因为 ?.
意味着空检查已经发生,那么如果 SomeMethod()
不是虚拟的,它将被编译为 call
。这只发生在 ?.
中,因为编译 ?.
的代码可以进行该检查,而 if (obj != null){obj.SomeMethod();}
不会发生,因为编译 .
的代码不知道它是在空检查之后。 涉及的逻辑非常本地化。
可以在 CIL 级别跳过对虚拟方法的虚拟 table 查找。这就是 base
呼叫的工作方式;编译为 call
上的方法实现,而不是 callvirt
。通过在构造 obj?.SomeMethod()
中扩展,其中 SomeMethod
是虚拟的和密封的(无论是单独的还是因为 obj
的类型被密封)然后理论上可以将其编译为 call
到最派生类型的实现。尽管在声明类型和密封类型之间的层次结构中添加或删除重写 classes 时,需要特别进行一些额外的检查以确保它仍然正常工作。它需要对层次结构有一些全局了解(并确保知识不会改变,这意味着当前正在编译的程序集中的所有类型)才能使优化安全。而且收益很小。而且大部分时间仍然不可用,原因与 callvirt
在大多数时间甚至在非虚拟呼叫中使用的原因相同。
我认为 sealed
不会在任何地方影响编译器生成调用的方式,而且大多数时候肯定不会影响它。
虽然抖动可以自由应用更多知识,但如果有的话,差异也会非常小。我当然建议将 classes 标记为 sealed
,如果抖动利用了它,那很好,但我推荐它的主要原因不是性能而是正确性。如果你在某个地方试图覆盖你标记为 sealed
的 class 那么要么 A. 你只是稍微改变了设计并且知道你必须删除 sealed
(. 5seconds work to remove it) or B. 你在一个地方做了一些你确信你不会在另一个地方做的事情。重新考虑的短暂停顿很好。
If I make a class sealed does this mean that all virtual methods in class hierarchy are also marked sealed?
它们被视为密封,就像明确标记为密封一样。