[英]Why do we need sealed classes?
我知道密封類的功能。 它不可繼承。 但我的問題是為什么我們需要一個密封類? 如果不是繼承屬性和方法是動機,為什么不將它們聲明為私有?
首先,讓我們從定義開始; 密封是一個修飾符,如果應用於類,則使其不可繼承,如果應用於虛擬方法或屬性,則使其不可超越。
public sealed class A { ... }
public class B
{
...
public sealed string Property { get; set; }
public sealed void Method() { ... }
}
其用法的一個示例是定義一個專門的類/方法或屬性,其中潛在的更改可以使它們按預期停止工作(例如,System.Drawing 命名空間的 Pens 類)。
...
namespace System.Drawing
{
//
// Summary:
// Pens for all the standard colors. This class cannot be inherited.
public sealed class Pens
{
public static Pen Transparent { get; }
public static Pen Orchid { get; }
public static Pen OrangeRed { get; }
...
}
}
因為密封類不能被繼承,所以它不能用作基類,因此抽象類不能使用密封修飾符。
同樣重要的是要提到結構是隱式密封的。
表現
要真正看到它們,您需要分析JIT 編譯的代碼(最后一個)。
C# 代碼
public sealed class Sealed
{
public string Message { get; set; }
public void DoStuff() { }
}
public class Derived : Base
{
public sealed override void DoStuff() { }
}
public class Base
{
public string Message { get; set; }
public virtual void DoStuff() { }
}
static void Main()
{
Sealed sealedClass = new Sealed();
sealedClass.DoStuff();
Derived derivedClass = new Derived();
derivedClass.DoStuff();
Base BaseClass = new Base();
BaseClass.DoStuff();
}
軍用代碼
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 41 (0x29)
.maxstack 8
IL_0000: newobj instance void ConsoleApp1.Program/Sealed::.ctor()
IL_0005: callvirt instance void ConsoleApp1.Program/Sealed::DoStuff()
IL_000a: newobj instance void ConsoleApp1.Program/Derived::.ctor()
IL_000f: callvirt instance void ConsoleApp1.Program/Base::DoStuff()
IL_0014: newobj instance void ConsoleApp1.Program/Base::.ctor()
IL_0019: callvirt instance void ConsoleApp1.Program/Base::DoStuff()
IL_0028: ret
} // end of method Program::Main
JIT- 編譯代碼
--- C:\Users\Ivan Porta\source\repos\ConsoleApp1\Program.cs --------------------
{
0066084A in al,dx
0066084B push edi
0066084C push esi
0066084D push ebx
0066084E sub esp,4Ch
00660851 lea edi,[ebp-58h]
00660854 mov ecx,13h
00660859 xor eax,eax
0066085B rep stos dword ptr es:[edi]
0066085D cmp dword ptr ds:[5842F0h],0
00660864 je 0066086B
00660866 call 744CFAD0
0066086B xor edx,edx
0066086D mov dword ptr [ebp-3Ch],edx
00660870 xor edx,edx
00660872 mov dword ptr [ebp-48h],edx
00660875 xor edx,edx
00660877 mov dword ptr [ebp-44h],edx
0066087A xor edx,edx
0066087C mov dword ptr [ebp-40h],edx
0066087F nop
Sealed sealedClass = new Sealed();
00660880 mov ecx,584E1Ch
00660885 call 005730F4
0066088A mov dword ptr [ebp-4Ch],eax
0066088D mov ecx,dword ptr [ebp-4Ch]
00660890 call 00660468
00660895 mov eax,dword ptr [ebp-4Ch]
00660898 mov dword ptr [ebp-3Ch],eax
sealedClass.DoStuff();
0066089B mov ecx,dword ptr [ebp-3Ch]
0066089E cmp dword ptr [ecx],ecx
006608A0 call 00660460
006608A5 nop
Derived derivedClass = new Derived();
006608A6 mov ecx,584F3Ch
006608AB call 005730F4
006608B0 mov dword ptr [ebp-50h],eax
006608B3 mov ecx,dword ptr [ebp-50h]
006608B6 call 006604A8
006608BB mov eax,dword ptr [ebp-50h]
006608BE mov dword ptr [ebp-40h],eax
derivedClass.DoStuff();
006608C1 mov ecx,dword ptr [ebp-40h]
006608C4 mov eax,dword ptr [ecx]
006608C6 mov eax,dword ptr [eax+28h]
006608C9 call dword ptr [eax+10h]
006608CC nop
Base BaseClass = new Base();
006608CD mov ecx,584EC0h
006608D2 call 005730F4
006608D7 mov dword ptr [ebp-54h],eax
006608DA mov ecx,dword ptr [ebp-54h]
006608DD call 00660490
006608E2 mov eax,dword ptr [ebp-54h]
006608E5 mov dword ptr [ebp-44h],eax
BaseClass.DoStuff();
006608E8 mov ecx,dword ptr [ebp-44h]
006608EB mov eax,dword ptr [ecx]
006608ED mov eax,dword ptr [eax+28h]
006608F0 call dword ptr [eax+10h]
006608F3 nop
}
0066091A nop
0066091B lea esp,[ebp-0Ch]
0066091E pop ebx
0066091F pop esi
00660920 pop edi
00660921 pop ebp
00660922 ret
雖然對象的創建是相同的,但調用密封類和派生/基類的方法所執行的指令略有不同。 將數據移入寄存器或RAM(mov指令)后,調用密封方法,執行dword ptr [ecx],ecx(cmp指令)之間的比較,然后調用該方法,而派生/基類直接執行該方法。 .
根據 Torbjöorn Granlund 撰寫的報告,AMD 和 Intel x86 處理器的指令延遲和吞吐量, Intel Pentium 4 中以下指令的速度為:
鏈接: https : //gmplib.org/~tege/x86-timing.pdf
編譯器的優化使密封和非密封分類的性能差異如此之低,以至於我們談論的是處理器圈,因此與大多數應用程序無關。
您可以將成員設為公開,以便類外的代碼可以使用它們。 無論是否存在繼承,這都可能發生。
一個類可能需要被密封的原因是繼承需要被設計。 類的作者必須考慮繼承代碼將以何種方式與基類代碼交互。
在我看來,在 C# 語言中使類默認可繼承是錯誤的。 Java 走得更遠,默認方法是virtual
,這在許多人看來是一個設計錯誤。
1.在一個實現安全功能的類上,使得原始對象不能被“冒充”。
2.更一般地說,我最近與微軟的一個人交流,他告訴我他們試圖將繼承限制在真正有意義的地方,因為如果不加以處理,它會在性能方面變得昂貴。 Sealed 關鍵字告訴 CLR 沒有進一步向下查找方法的類,這會加快速度。
在當今市場上的大多數性能增強工具中,您會發現一個復選框,用於密封所有未繼承的類。 不過要小心,因為如果您想通過 MEF 允許插件或程序集發現,您會遇到問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.