你啥时候以及为啥要封课?

Posted

技术标签:

【中文标题】你啥时候以及为啥要封课?【英文标题】:When and why would you seal a class?你什么时候以及为什么要封课? 【发布时间】:2011-12-08 07:43:44 【问题描述】:

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

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

我的意思是要小心密封重用库中的类,特别是如果它们被第三方重用然后重新集成(通过 MEF)到代码库中。您的代码库可能不会继承给定的类,但第三方会。 原因 #1 听起来很模糊,但假设我们大部分时间不编写“安全功能”,这是否意味着原因 #1 几乎不适用?原因 #2 用于性能调整。我们在谈论多少性能差异?它们是否足以证明改变非安全类定义的合理性?即使答案是“是”,理想情况下这也是一个编译器选项,即“为所有非密封类生成优化代码”,而不是让我们的开发人员更改代码库。 如果不加以处理,性能会变得很昂贵这甚至可以用更少的疯狂测试来衡量吗? 密封很烂。它使测试变得更加困难 - 我想用 FakeItEasy 模拟几个 ASP.NET 类,但我不能,因为它们是密封的。 完全同意@RayLuo。我打了好几次,人们在安全性和性能不是真正问题的情况下密封了他们的课程。他们的“密封”只是阻止了我合理的覆盖课程的需要,使事情变得更加困难。就像 Warlike Chimpanzee 所说的那样,在测试中嘲笑一个班级是如此普遍。【参考方案2】:

Louis Kottmann's excellent answer 的附录:

    如果一个类不是为继承而设计,子类可能会破坏class invariants。当然,这只适用于创建公共 API 的情况,但根据我的经验,我会密封任何未明确设计为子类的类。

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

然后是这样的:

    密封会使单元测试更加困难,因为它禁止模拟。

一些相关链接:

Effective Java, 2nd Edition 约书亚·布洛赫。请参阅第 17 项(需要 Safari 订阅) Effective Java Item 17: Design and document for inheritance or else prohibit it(同题讨论)

还要注意Kotlin 默认密封类; its open keyword is the opposite of Java's final or the sealed of C#。 (可以肯定的是,there is no universal agreement that this is a good thing。)

【讨论】:

密封类带来的麻烦多于好处。我不断发现开发人员密封类的情况,导致我在应该简单的事情上遇到数小时的困难。别再封印课了,你没你想的那么机智。仅当您必须重新考虑时才密封课程。只是我的意见,作为一个必须处理其他人的密封类我无法编辑/解封的人。 @GantMan 的评论实际上应该被视为对 OP 问题的答案之一,因为它基本上给出了一个答案:“什么时候?几乎没有。为什么?这就是为什么你不这样做的原因去做。”授予您应该将您的评论作为单独的答案重新发布,然后为其收集选票。 :-) 这是否参考了这个答案:***.com/a/7777674/3195477?链接到它比(仅)命名该人更好【参考方案3】:

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

很多时候,当我们设计一个具有固定行为的实用程序类时,密封类也是有意义的,我们不想改变它。

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

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

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

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

【讨论】:

如何扩展 String 类是不可取的? String 仍然可以按照现在的方式工作,并且您可以在需要时拥有具有附加功能的派生类,那么您在说什么问题? 另外,“封顶”您的继承层次结构的意义何在?这意味着如果您确实需要扩展该层次结构,则必须先解封父类,这只是效率低下 查看 Eric Lippert 的 excellent post 和 SO question。 即使这个答案基本上可以归结为“你为什么要派生 String?”,然后继续提到你可能想要派生 String 的原因(例如以 null 结尾的字符串)并说你应该在没有继承的情况下解决它。那么,为什么要让它变得更复杂,并且必须在以后解决它,因为您可以简单地将它放在首位并让您的选择保持开放 我内心的怀疑者看到一篇发表于 2004 年的文章,很好奇 Eric 的想法可能在过去 15 年中发生了变化。编辑:好吧,根据您引用的very link,至少 MS 官方立场是这样的。【参考方案4】:

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

【讨论】:

欢迎提供解决方案链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted. 对不起,我不打算将其发布为答案,但它似乎与其他答案无关,我不知道该放在哪里 我根据建议编辑了帖子。本来我只是想贡献一个不同的角度(也许),但我只是投了反对票,我们还没有谈论内容,-1 可以告诉我原因吗?

以上是关于你啥时候以及为啥要封课?的主要内容,如果未能解决你的问题,请参考以下文章

你啥时候使用 java.util.LinkedList [重复]

你啥时候乘以 Unity 中的 Time.deltaTime?

你啥时候乘以 Unity 中的 Time.deltaTime?

你啥时候使用“受保护的内部”访问修饰符?

详述图片base64加密的原理,告诉你啥是"/9j/"

你啥时候更喜欢 DateTime 而不是 DateTimeOffset