Ree*_*sey 38
在最低级别,编译器可以在密封类时进行微优化.
如果要在密封类上调用方法,并且在编译时将类型声明为密封类,则编译器可以使用调用IL指令而不是callvirt IL指令来实现方法调用(在大多数情况下).这是因为无法覆盖方法目标.调用消除了空检查,并且比callvirt执行更快的vtable查找,因为它不必检查虚拟表.
这可以是对性能的非常非常小的改进.
话虽这么说,在决定是否密封课程时,我会完全忽略这一点.标记密封的类型确实应该是设计决策,而不是性能决策.您是否希望现在或将来人们(包括您自己)可能从您的班级继承?如果是这样,请不要密封.如果没有,请密封.这确实应该是决定因素.
决定发布小代码示例,以说明C#编译器何时发出"call"和"callvirt"指令.
那么,这里是我使用的所有类型的源代码:
public sealed class SealedClass
{
public void DoSmth()
{ }
}
public class ClassWithSealedMethod : ClassWithVirtualMethod
{
public sealed override void DoSmth()
{ }
}
public class ClassWithVirtualMethod
{
public virtual void DoSmth()
{ }
}
Run Code Online (Sandbox Code Playgroud)
我还有一个方法调用所有"DoSmth()"方法:
public void Call()
{
SealedClass sc = new SealedClass();
sc.DoSmth();
ClassWithVirtualMethod cwcm = new ClassWithVirtualMethod();
cwcm.DoSmth();
ClassWithSealedMethod cwsm = new ClassWithSealedMethod();
cwsm.DoSmth();
}
Run Code Online (Sandbox Code Playgroud)
看看"Call()"方法我们可以说(理论上)C#编译器应该发出2个"callvirt"和1个"call"指令,对吧?不幸的是,现实有点不同 - 3"callvirt"-s:
.method public hidebysig instance void Call() cil managed
{
.maxstack 1
.locals init (
[0] class TestApp.SealedClasses.SealedClass sc,
[1] class TestApp.SealedClasses.ClassWithVirtualMethod cwcm,
[2] class TestApp.SealedClasses.ClassWithSealedMethod cwsm)
L_0000: newobj instance void TestApp.SealedClasses.SealedClass::.ctor()
L_0005: stloc.0
L_0006: ldloc.0
L_0007: callvirt instance void TestApp.SealedClasses.SealedClass::DoSmth()
L_000c: newobj instance void TestApp.SealedClasses.ClassWithVirtualMethod::.ctor()
L_0011: stloc.1
L_0012: ldloc.1
L_0013: callvirt instance void TestApp.SealedClasses.ClassWithVirtualMethod::DoSmth()
L_0018: newobj instance void TestApp.SealedClasses.ClassWithSealedMethod::.ctor()
L_001d: stloc.2
L_001e: ldloc.2
L_001f: callvirt instance void TestApp.SealedClasses.ClassWithVirtualMethod::DoSmth()
L_0024: ret
}
Run Code Online (Sandbox Code Playgroud)
原因很简单:运行时必须在调用"DoSmth()"方法之前检查类型实例是否不等于null. 但我们仍然可以编写代码,使C#编译器能够发出优化的IL代码:
public void Call()
{
new SealedClass().DoSmth();
new ClassWithVirtualMethod().DoSmth();
new ClassWithSealedMethod().DoSmth();
}
Run Code Online (Sandbox Code Playgroud)
结果是:
.method public hidebysig instance void Call() cil managed
{
.maxstack 8
L_0000: newobj instance void TestApp.SealedClasses.SealedClass::.ctor()
L_0005: call instance void TestApp.SealedClasses.SealedClass::DoSmth()
L_000a: newobj instance void TestApp.SealedClasses.ClassWithVirtualMethod::.ctor()
L_000f: callvirt instance void TestApp.SealedClasses.ClassWithVirtualMethod::DoSmth()
L_0014: newobj instance void TestApp.SealedClasses.ClassWithSealedMethod::.ctor()
L_0019: callvirt instance void TestApp.SealedClasses.ClassWithVirtualMethod::DoSmth()
L_001e: ret
}
Run Code Online (Sandbox Code Playgroud)
如果你尝试以同样的方式调用非密封类的非虚方法,你也会得到"call"指令而不是"callvirt"