设计建议 - 何时有效使用“虚拟”和“密封”

时间:2010-01-07 20:21:54

标签: c# inheritance

我正在编写一个C#网络库(主要是作为一种学习练习,如果有人实际上最终使用它,那对我来说并不重要,因为我确信解决方案已经存在)。

到目前为止,我对我的结构非常满意......我有几层客户端/服务器,可以通过套接字以原始字节进行通信,或者通过序列化消息对象稍微复杂一些。

我遇到的问题(问题?)我应该在什么时候声明方法,属性或事件sealedvirtual,或者没有限定符。

我知道所有这些都做了 - sealed阻止了类的继承,或者进一步覆盖了一个方法。 virtual将通过方法覆盖允许多态行为。

因为我正在设计一个类库,所以我不确定何时才能使用它们。这是一个可扩展性的问题,我认为......我提供了一些接口,一个或两个抽象类,以及我的库的消费者使用或扩展的一些具体实现,但是我很难确定什么时候它是一个“好主意”明确禁止派生类或允许覆盖功能。

在设计我的课程以供其他人使用时,请记住哪些一般指示或建议?

This questionthis one有点帮助,this one也是如此,但由于我正在编写一个可分发的库,我试图覆盖我的所有基础。

8 个答案:

答案 0 :(得分:25)

Microsoft Design Guidelines for Developing Class Libraries开始,尤其是Extensibility部分,您可以在{{}}和virtual members找到文章。

引用,在这里:

  • 除非您有充分理由这样做,并且您了解与设计,测试和维护虚拟成员相关的所有成本,否则不要将成员设置为虚拟成员。
  • 对虚拟成员的公共可访问性更喜欢受保护的可访问性。公共成员应通过调用受保护的虚拟成员来提供可扩展性(如果需要)。

  • 如果没有充分的理由,请不要上课。

  • 请勿在密封类型上声明受保护或虚拟成员。
  • 考虑密封您覆盖的成员。

阅读完整的文章。

答案 1 :(得分:20)

首先,我同意其他答案,这些答案表明积极密封任何非专门设计用于扩展的东西。我不同意你需要一个封印东西的理由;相反,你需要一个理由留下未密封的东西。

您的问题很一般,所以这里有一个通用的答案:您的库可能是功能的集合,可以用作解决用户问题的工具。您的库中的每个功能都可能是因为您进行了一些研究,发现存在需要解决的用户问题,并为他们解决了这个问题。

这些功能中的每一项都有一定的成本。其中一些成本是过去的 - 您花费在设计,实现,测试,调试和发送代码上的时间。其中一些成本尚未到来:维护,阅读错误报告等。还有更微妙的成本,比如可能保持与现有功能的向后兼容性将提高明天实施新功能的成本。

可扩展性也是一项功能。这是一个功能,如果你弄错了,成本几乎完全在 future 。像任何其他功能一样对待它:弄清楚它是否是您的用户真正需要的功能,以及它的好处是否支付其成本。如果您无法清楚地评估可扩展性的好处或成本,那么不小心实施它的风险很大。

答案 2 :(得分:7)

在我看来,你无法真正预见到你的用户最终会使用它。因此,除非存在一些您不希望用户捣乱的内部行为(即他们需要知道必须先在此之前设置,等等),否则通常不要密封任何东西。

对于虚拟,您倾向于制作最终用户可能想要覆盖的任何虚拟内容。事件大大减少了对虚拟功能的需求,但仍有时候你想让它们成为虚拟功能。通常,您需要考虑最终用户可能需要定制任何给定成员函数的方式。

答案 3 :(得分:6)

有很多次我说过“该死的!为什么这堂课被密封了??”,但我从来没有说过“天哪 - 我希望他们能够密封那堂课!”

有一天,一个比你更好的程序员想要扩展你的课程并且知道他们正在做什么的机会很大。我认为有很好的理由密封。

答案 4 :(得分:4)

声明方法虚拟意味着某个合同:您的类接受它可以被覆盖并预期。

通常有一个明确的理由让虚拟的东西。如有疑问:不要。

密封正好相反,你可以说你不想再被覆盖了。次要原因可能是这里的表现,但我不会太快使用它。

答案 5 :(得分:3)

通常,通过继承设计可扩展性类并不容易。当你密封某些东西时,你说这个类实际上不适合实现继承(例如,你可能会使用不安全的代码做一堆低级的东西)。

为了获得最大的可组合性(在数学意义上),我建议密封所有不是抽象类的东西。实际上,这就是如何在语言中实现代数数据类型(在本例中为Sum类型)(请查看http://blog.lab49.com/archives/3011以获取令人兴奋的文章)。

答案 6 :(得分:3)

有争议的意见:C# classes should be sealed by default

如果这个类不是为了继承而你没有考虑到潜在的陷阱或记录如何正确继承,那么如果人们覆盖方法,那么你的课很可能会以奇怪的方式失败。当你没有密封你的课程时,你告诉你班级的客户可以从中获得,你将来必须支持你班级的这个“界面”,以避免破坏选择继承你班级的客户。这限制了您将来如何修改课程。

因此默认情况下密封类,除非您明确要允许它。如果你想要允许它,请确保通过创建虚拟文档来记录应该覆盖哪些方法,以及重写的方法必须做些什么来确保类继续工作(例如调用基本方法)。

答案 7 :(得分:2)

您有兴趣冲突

  • 如果您希望让来电者轻松测试他们的代码(例如进行测试驱动开发),那么您的用户需要能够模拟您的课程。
    • 所以所有方法都必须是虚拟的,或者您必须具有包含所有方法的实现接口。
  • 但是,如果您希望明确您在用户可能创建的任何子类上提供的合同,那么您应该遵守Microsoft准则。
    • 除非您有充分理由这样做并且您了解与设计,测试和维护虚拟成员相关的所有成本,否则不要将成员设置为虚拟 遗憾的是,除了模拟之外,没有办法标记虚拟方法,表示你不希望任何客户端代码实现它。