简体   繁体   English

为什么我们需要密封类?

[英]Why do we need sealed classes?

I know the functionality of a Sealed class.我知道密封类的功能。 That its not inheritable.它不可继承。 But my question is why do we need a Sealed Class?但我的问题是为什么我们需要一个密封类? If not inheriting properties and methods is the motive, why don't just declare them as private?如果不是继承属性和方法是动机,为什么不将它们声明为私有?

First of all, let's start with a definition;首先,让我们从定义开始; sealed is a modifier which if applied to a class make it non-inheritable and if applied to virtual methods or properties makes them non-ovveridable.密封是一个修饰符,如果应用于类,则使其不可继承,如果应用于虚拟方法或属性,则使其不可超越。

public sealed class A { ... }
public class B 
{
    ...
    public sealed string Property { get; set; }
    public sealed void Method() { ... }
}

An example of its usage is to define a specialized class/method or property in which potential alterations can make them stop working as expected (for example, the Pens class of the System.Drawing namespace).其用法的一个示例是定义一个专门的类/方法或属性,其中潜在的更改可以使它们按预期停止工作(例如,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; }
        ...
    }
}

Because a sealed class cannot be inherited, it cannot be used as base class and by consequence, an abstract class cannot use the sealed modifier.因为密封类不能被继承,所以它不能用作基类,因此抽象类不能使用密封修饰符。

It's also important to mention that structs are implicitly sealed.同样重要的是要提到结构是隐式密封的。

Performance表现

To really see them you need to analyze the JIT-Compiled cod e (last one).要真正看到它们,您需要分析JIT 编译的代码(最后一个)。

C# Code 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();
}

MIL Code军用代码

.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- Compiled Code 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  

While the creation of the objects is the same, the instruction executed to invoke the methods of the sealed and derived/base class are slightly different.虽然对象的创建是相同的,但调用密封类和派生/基类的方法所执行的指令略有不同。 After moving data into registers or RAM (mov instruction), the invoke of the sealed method, execute a comparison between dword ptr [ecx],ecx (cmp instruction) and then call the method while the derived/base class execute directly the method..将数据移入寄存器或RAM(mov指令)后,调用密封方法,执行dword ptr [ecx],ecx(cmp指令)之间的比较,然后调用该方法,而派生/基类直接执行该方法。 .

According to the report written by Torbj¨orn Granlund, Instruction latencies and throughput for AMD and Intel x86 processors , the speed of the following instruction in a Intel Pentium 4 are:根据 Torbjöorn Granlund 撰写的报告,AMD 和 Intel x86 处理器的指令延迟和吞吐量, Intel Pentium 4 中以下指令的速度为:

  • mov : has 1 cycle as latency and the processor can sustain 2.5 instructions per cycle of this type mov : 有 1 个周期作为延迟,处理器每个周期可以维持 2.5 条这种类型的指令
  • cmp : has 1 cycle as latency and the processor can sustain 2 instructions per cycle of this type cmp : 有 1 个周期作为延迟,处理器每个周期可以维持 2 条这种类型的指令

Link : https://gmplib.org/~tege/x86-timing.pdf链接https : //gmplib.org/~tege/x86-timing.pdf

The optimization of the compilers have made the difference between the performances of a sealed and not-sealed classed so low that we are talking about processor circles and for this reason are irrelevant for the majority of applications.编译器的优化使密封和非密封分类的性能差异如此之低,以至于我们谈论的是处理器圈,因此与大多数应用程序无关。

You can leave members public so that code outside of the class can use them.您可以将成员设为公开,以便类外的代码可以使用它们。 This can happen irrespective of whether there is inheritance or not.无论是否存在继承,这都可能发生。

The reason a class might need to be sealed is that inheritance needs to be designed for.一个类可能需要被密封的原因是继承需要被设计。 The author of a class must take into consideration in what way inheriting code will interact with the base class code.类的作者必须考虑继承代码将以何种方式与基类代码交互。

In my opinion it is a mistake in the C# language to make classes inheritable by default.在我看来,在 C# 语言中使类默认可继承是错误的。 Java went even further and made methods virtual by default which appears to many as a design mistake. Java 走得更远,默认方法是virtual ,这在许多人看来是一个设计错误。

1.On a class that implements security features, so that the original object cannot be "impersonated". 1.在一个实现安全功能的类上,使得原始对象不能被“冒充”。

2.More generally, I recently exchanged with a person at Microsoft, who told me they tried to limit the inheritance to the places where it really made full sense, because it becomes expensive performance-wise if left untreated. 2.更一般地说,我最近与微软的一个人交流,他告诉我他们试图将继承限制在真正有意义的地方,因为如果不加以处理,它会在性能方面变得昂贵。 The sealed keyword tells the CLR that there is no class further down to look for methods, and that speeds things up. Sealed 关键字告诉 CLR 没有进一步向下查找方法的类,这会加快速度。

In most performance-enhancing tools on the market nowadays, you will find a checkbox that will seal all your classes that aren't inherited.在当今市场上的大多数性能增强工具中,您会发现一个复选框,用于密封所有未继承的类。 Be careful though, because if you want to allow plugins or assembly discovery through MEF, you will run into problems.不过要小心,因为如果您想通过 MEF 允许插件或程序集发现,您会遇到问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM