C#中的密封类

时间:2018-09-13 12:53:54

标签: c#-4.0 sealed-class

我知道Sealed类的功能。那是不可继承的。但是我的问题是为什么我们需要密封班? 如果动机不是继承属性和方法,为什么不仅仅将它们声明为私有?

3 个答案:

答案 0 :(得分:2)

首先,让我们从定义开始。密封是一个修饰符,如果将其应用于类使其不可继承,并且如果将其应用于虚拟方法或属性将使其变为不可遍历。

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编译的编码 e(最后一个)。

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代码

.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(移动指令)后,调用密封方法,在dword ptr [ecx],ecx(cmp指令)之间执行比较,然后在派生/基类直接执行该方法的同时调用该方法。

根据TorbjörornGranlund撰写的报告, AMD和Intel x86处理器的指令等待时间和吞吐量,Intel Pentium 4中以下指令的速度为:

  • mov :具有1个周期的等待时间,处理器每个此类周期可支持2.5条指令
  • cmp :具有1个周期的延迟,处理器可以在这种类型的每个周期维持2条指令

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

编译器的优化使密封和未密封的分类之间的性能差异变得如此之低,以至于我们谈论的是处理器界,因此与大多数应用程序无关。

答案 1 :(得分:0)

您可以将成员保持公开状态,以便课程外的代码可以使用它们。无论是否存在继承,都可能发生这种情况。

可能需要密封类的原因是需要为其设计继承。类的作者必须考虑继承代码将如何与基类代码交互。

在我看来,在C#语言中,默认情况下使类可继承是一个错误。 Java更进一步,默认情况下使方法virtual成为很多设计错误。

答案 2 :(得分:0)

1。在实现安全功能的类上,这样原始对象就不能被“模拟”。

2。更笼统地说,我最近与Microsoft的一个人进行了交流,他告诉我,他们试图将继承限制在真正有意义的地方,因为如果不加以处理,它将在性能上变得昂贵。 Sealed关键字告诉CLR,没有其他类可以查找方法,并且可以加快处理速度。

在当今市场上大多数性能增强工具中,您会发现一个复选框,可以密封所有未继承的类。 不过请小心,因为如果您希望通过MEF允许插件或程序集发现,则会遇到问题。