您什么时候以及为什么要上课?


87

在C#和C ++ / CLI中,关键字sealed(或NotInheritable在VB中)用于保护类免受任何继承机会(该类是不可继承的)。我知道面向对象编程的一个功能是继承,我觉得使用sealed与该功能相反的功能会停止继承。是否有一个示例可以说明sealed使用它的好处以及何时使用它很重要?

Answers:


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

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

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


3
我的意思是在重用的库中密封类时要小心,尤其是如果它们被第三方重用,然后(通过MEF)重新集成到代码库中时。您的代码库可能不会继承给定的类,但第三方会继承。
Louis Kottmann

10
原因1听起来很含糊,但是,假设我们大多数时候都不编写“安全功能”,这是否意味着原因1几乎不适用?原因2是为了进行性能调整。我们在谈论多少性能差异?它们是否足够重要,足以证明改变非安全类的定义?即使答案是“是”,理想情况下也应该是编译器选项,即“为所有非密封类生成优化的代码”,而不是让我们的开发人员更改代码库。
RayLuo

1
如果不进行治疗,那么即使是用不到疯狂数量的疯狂测试就可以衡量,性能也会变得昂贵
t3chb0t

4
密封糟透了。它使测试更加困难-我想用FakeItEasy模拟几个ASP.NET类,但是我不能,因为它们是密封的。
好战的黑猩猩

2
我完全同意@RayLuo。我曾多次碰到人们在安全和性能并不是真正问题的情况下封闭了他们的课程。他们的“封印”只是阻止了我合理地改写课程,使事情变得更加困难。就像Warlike黑猩猩说的那样,嘲笑类在测试中是如此普遍。
ZZY

15

狒狒出色答案的附录:

  1. 如果一个类不是为继承而设计的,则子类可能会破坏类不变式。当然,这实际上仅适用于创建公共API的情况,但是根据我的经验法则,我密封未明确设计为子类的任何类。

在相关说明中,仅适用于未密封的类:创建的任何方法virtual都是扩展点,或者至少看起来像它应该是扩展点。声明方法也virtual应该是一个有意识的决定。(在C#中,这是一个有意识的决定;在Java中不是。)


编辑:一些相关链接:

还请注意,默认情况下,Kotlin会密封类。它的open关键字与JavafinalsealedC#相对。(可以肯定的是,没有普遍的共识,这是一件好事。)


25
密封类导致的头痛多于收益。我一直在发现开发人员已经密封类的情况,这使我在应该简单的事情上花了很多时间。停止上课,你不像你想的那样聪明。只有在必须且甚至重新考虑的情况下,才能密封课程。只是我的看法,作为必须处理别人无法密封/无法打开的密封课程的人。
甘特·劳德

9
@GantMan的评论实际上应被视为对OP的问题的答案之一,因为它实质上给出的答案是“何时?几乎没有。为什么?这就是为什么您不这样做的原因”。格兰特,您应该将评论重新发布为单独的答案,然后为其收集投票。:-)
RayLuo

1
这是否与以下答案相对应:stackoverflow.com/a/7777674/3195477 ?与其链接比(仅)命名此人更好
UuDdLrLrSs

2

将类标记为Sealed可以防止篡改可能危及安全性或影响性能的重要类。

很多时候,在设计具有固定行为的实用程序类时,密封类也是有意义的,而我们不想更改。

例如,中的System命名空间C#提供了许多密封的类,例如String。如果没有密封,则有可能扩展其功能,这可能是不希望的,因为它是具有给定功能的基本类型。

同样,structuresC#中总是隐式密封。因此,不能从另一个结构派生一个结构/类。这样做的原因是,structures它们仅用于建模独立的,原子的,用户定义的数据类型,而我们不想修改它们。

有时,当您构建类层次结构时,您可能想根据域模型或业务规则来限制继承链中的某个分支。

例如,aManagerPartTimeEmployee都是Employees,但是您在组织中的兼职员工之后没有任何作用。在这种情况下,您可能需要密封PartTimeEmployee以防止进一步分支。另一方面,如果您有每小时或每周的兼职员工,则可以从继承PartTimeEmployee


扩展String类怎么会不受欢迎?String仍然可以像现在一样正常工作,并且您可以在需要时使用带有附加功能的派生类,那么您在谈论什么问题?
凯文·威尔斯

另外,“限制”继承层次结构的意义何在?这意味着,如果您确实需要扩展该层次结构,则必须先取消对父类的密封,这效率很低
Kevin Wells

请查看Eric Lippert的这篇精彩文章以及有关SO的问题
阿克沙伊霍特(Akshay Khot)

1
即使该答案基本上可以归结为“为什么要派生String?”,然后继续提及您可能想要派生String(例如,以空终止的字符串)的原因,并说您应该在不继承的情况下解决它。那么,为什么要使其变得更复杂而又不得不稍后解决它,而您却可以简单地将其放在第一位而不密封,而让您的选择保持打开状态
Kevin Wells

对于第二个问题,目标是防止不良行为(取决于业务逻辑)。unseal如果需要的话,以后对类进行比较容易,而不是密封它并破坏依赖于它的所有类。
阿克沙伊霍特(Akshay Khot)

0

我认为这篇文章有一些好处,具体情况是,当尝试将非密封类强制转换为任何随机接口时,编译器不会抛出错误。但是当使用密封时,编译器会抛出无法转换的错误。密封类带来了额外的代码访问安全性。
https://www.codeproject.com/Articles/239939/Csharp-Tweaks-Why-to-use-the-sealed-keyword-on-cla


1
欢迎使用指向解决方案的链接,但是请确保没有该链接的情况下您的回答是有用的:在该链接周围添加上下文,以便您的其他用户可以了解它的含义和含义,然后引用您所使用页面中最相关的部分如果目标页面不可用,请重新链接。只是链接的答案可能会被删除。
鲍姆·米特·奥根

抱歉,我无意将其发布为答案,但是它似乎与其他答案无关,而且我也不知道该放在哪里
strisunshine

1
我根据建议编辑了帖子。本来我只是想提供一个不同的角度(也许),但是我只得到了赞成票,而我们还没有讨论内容,-1可以告诉我们原因吗?
strisunshine '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.