前言
最近在 dotnet/sdk 上看到一個 Issue,它提出了一個有趣的要求:默認情況下將類設置為密封類(Sealed)?
什么是密封類?
默認情況下,類是開放的,這意味著它是可以被繼承的。例如:
class?BaseClass
{public?virtual?string?Method(){return?"BaseClass.Method";}
}class?DerivedClass?:?BaseClass
{public?override?string?Method(){return?"DerivedClass.Method";}
}class?AnotherClass?:?DerivedClass
{//...
}
而密封類是一種特殊的類,它使用sealed
修飾符阻止其他類繼承自該類。例如:
sealed?class?SealedClass?:?BaseClass
{public?override?string?Method(){return?"SealedClass.Method";}
}
那為什么要將類默認設為密封類呢?答案是——性能。
Benchmark
首先創建一個控制臺程序,并添加 BenchmarkDotNet 包。
然后創建一個性能測試類:
public?class?PerformanceBenchmark
{private?readonly?DerivedClass?derivedClass?=?new?DerivedClass();private?readonly?SealedClass?sealedClass?=?new?SealedClass();[Benchmark]public?void?DerivedClass_Method()?=>?derivedClass.Method();[Benchmark]public?void?SealedClass_Method()?=>?sealedClass.Method();?
}
并且修改Program.cs
文件,添加如下代碼:
static?void?Main(string[]?args)
{var?summary?=?BenchmarkRunner.Run<PerformanceBenchmark>();
}
最后,運行程序,查看結果:
dotnet?run?-c?Release
可以看到,SealedClass_Method
方法執行得明顯快很多。
原理
為什么密封類比普通類執行得快呢?讓我們看看 JIT 生成的代碼:
可以看到,當調用密封類中的重寫方法時,是直接在密封類對象的內存地址上完成的。而當調用普通類中的重寫方法時,卻需要額外的mov
指令,這是因為普通類的內存地址是不確定的,需要先獲取到對象的類型,然后再從對應類型中獲取方法。
總結
在本文中,我們了解了密封類有更好的性能。也就是說,如果你的類不會被繼承,那么請將它設置為密封類。
添加微信號【MyIO666】,邀你加入技術交流群