Intro最近看到一篇文章Performancebenefitsofsealedclassin.NET,觉得写的不错,翻译一下,分享给大家。其实目前我看到的很多类库都没有考虑使用密封类。如果你的类型不想被继承,或者不需要重写,那么你应该考虑将它声明为密封类,尤其是类库。对于项目的作者来说,这其实是一件值得考虑的事情。很多优秀的类库都会考虑这样的问题,尤其是.NET框架中的一些代码。大家在看开源项目源码的时候也可以关注一下。默认情况下,前言类是不密封的。这意味着您可以继承它们。我认为这不是正确的默认行为。事实上,除非一个类被设计成可以继承的,否则它应该是密封的。如果需要,您仍然可以稍后移除密封修饰符。除了不是最佳默认设置外,它还会影响性能。事实上,当一个类被密封时,JIT可以做一些优化,稍微提高应用程序的性能。在.NET7中应该有一个新的分析器来检测可以密封的类。在本文中,我将展示本期https://github.com/dotnet/runtime/issues/49944中提到的密封类的一些性能优势。性能优势虚拟方法调用当调用虚拟方法时,会在运行时根据对象的实际类型找到实际方法。每种类型都有一个虚方法表(vtable),里面包含了所有虚方法的地址。这些指针在运行时用于调用适当的方法实现(动态执行)。如果JIT知道对象的实际类型,它可以跳过vtable并直接调用正确的方法以提高性能。使用密封类型有助于JIT,因为它知道不能有任何派生类。公共类SealedBenchmark{只读NonSealedTypenonSealedType=new();只读SealedTypesealedType=new();[Benchmark(Baseline=true)]publicvoidNonSealed(){//JIT无法知道nonSealedType的实际类型。实际上,//它可能已通过另一种方法设置为派生类。//所以,为了安全,它必须使用虚拟调用。nonSealedType.Method();}[Benchmark]publicvoidSealed(){//JIT确定sealedType是一个SealedType。由于该类是密封的,//它不能是派生类型的实例。//所以它可以使用更快的直接调用。sealedType.Method();}}internalclassBaseType{publicvirtualvoidMethod(){}}internalclassNonSealedType:BaseType{publicoverridevoidMethod(){}}internalsealedclassSealedType:BaseType{publicoverridevoidMethod(){}}方法计算平均值错误方差中位数比代码大小NonSealed0.4465ns0.0276ns0.0258ns0.4437ns1.0018BSealed0.0107ns0.0160ns0.0150ns0.0000ns0.027B请注意,当JIT可以确定实际类型时,即使类型未密封,也可以使用直接调用。例如,下面两个片段之间没有区别:voidNonSealed(){varinstance=newNonSealedType();实例.方法();//JIT知道`instance`是NonSealedType因为它在方法中设置//并且从未修改过,所以它使用直接调用}voidSealed(){varinstance=newSealedType();实例.方法();//JIT知道instance是SealedType,所以直接调用}对象类型转换(is/as)当对象类型转换时,CLR必须在运行时检查对象的类型。转换为未密封类型时,运行时必须检查层次结构中的所有类型。但是,当转换为密封类型时,运行时必须只检查对象的类型,因此速度更快。公共类SealedBenchmark{只读BaseTypebaseType=new();[基准(基线=真)]publicboolIs_Sealed()=>baseType是SealedType;[基准测试]publicboolIs_NonSealed()=>baseTypeisNonSealedType;}internalclassBaseType{}internalclassNonSealedType:BaseType{}internalsealedclassSealedType:BaseType{}方法平均误差方差中位数Is_NonSealed1.6560ns0.0223ns0.0208ns1.00Is_Sealed0.1505ns0.0221ns0.0207ns0.09阵列。NET数组是协变的。这意味着,BaseType[]value=newDerivedType[1]是有效的。这不是其他集合的情况。例如,Listvalue=newList();是无效的。协变会带来性能损失。事实上,JIT在将项目分配给数组之前必须检查对象的类型。使用密封类型时,JIT可以取消检查。您可以查看JonSkeet的文章https://codeblog.jonskeet.uk/2013/06/22/array-covariance-not-just-ugly-but-slow-too/了解有关性能损失的更多详细信息。NonSealedType[]nonSealedTypeArray=newNonSealedType[100];[基准(基线=真)]publicvoidNonSealed(){nonSealedTypeArray[0]=newNonSealedType();}[基准]publicvoidSealed(){sealedTypeArray[0]=newSealedType();}}internalclassBaseType{}internalclassNonSealedType:BaseType{}internalsealedclassSealedType:BaseType{}方法均方差方法均方差中位数比NonSealed3.420ns0.0897ns0.0881ns1.0044BSealed2.951ns0.0781ns0.0802ns0.8658B数组转换为Span您可以将数组转换为Span或ReadOnlySpan。出于与上一节相同的原因,JIT必须在将数组转换为跨度之前检查对象的类型。使用密封型时,可以避免检查并略微提高性能。公共类SealedBenchmark{SealedType[]sealedTypeArray=newSealedType[100];NonSealedType[]nonSealedTypeArray=newNonSealedType[100];[基准(基线=真)]publicSpan
