2010-01-07 30 views
30

我正在写一个C#网络库(主要是作为一个学习锻炼,如果有人实际上最终使用它,因为我”这不是对我来说太重要当然,解决方案已经在那里了)。设计咨询 - 当使用“虚拟”和“密封”有效

我对自己的结构非常愉快,到目前为止,我还提供客户机/服务器,可以在原始字节通过套接字,或者稍微复杂一点,通过序列化消息对象沟通的几层。

我遇到的问题是什么时候我应该声明方法,属性或事件sealed,virtual或没有限定符。

我知道所有的这些事情 - sealed防止一类的继承,或方法的进一步覆盖。 virtual将允许通过方法重写的多态行为。

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

在设计我的课程供其他人使用时,请牢记一切指示或建议吗?

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

+1

(回复评论说,删除了!) @(节录) - 不正确的;您可以将'override'标记为'sealed'以防止进一步覆盖。 – 2010-01-07 20:28:43

回答

24

Microsoft Design Guidelines for Developing Class Libraries开始,尤其是Extensibility部分,您将在此找到有关virtual memberssealing的文章。

引用,在这里:

  • 不要让虚拟的成员,除非你有一个很好的理由这样做,你知道所有有关设计,测试的成本,并保持虚拟成员。
  • 为虚拟会员更好地选择受保护的可访问性而不是公共可访问性。公共成员应通过调用受保护的虚拟成员来提供可扩展性(如果需要)。

  • 如果没有充分的理由,请勿封印课程。

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

阅读完整的文章,虽然。

+0

将其留给MSDN。这些文章是非常有用的,我会一定让他们收藏。谢谢! :) – Sapph 2010-01-07 21:04:18

+2

该MSDN文章说,不要在性能密集型领域使用代表和事件,但仍有多准确(考虑文章是从2005年开始的)?从我公认的有限的测试中,调用委托与调用接口方法一样快,这比静态方法慢两倍。 – JulianR 2010-01-07 22:10:16

+0

朱利安,这改变了.NET 2.0:http://www.sturmnet.org/blog/2005/09/01/所以你是对的;那已经过时了。 – 2010-01-07 22:17:22

2

通过继承来设计可扩展性的类通常并不容易。当你说出某些东西时,你说这个类真的不适合实现继承(也许你正在做一堆低级别的东西,例如不安全的代码)。

为了获得最大的composibility(在数学意义上),我建议密封一切,这不是一个抽象类。实际上,这是在语言中实现代数数据类型(在这种情况下为总和类型)的方式(请查阅http://blog.lab49.com/archives/3011以获得令人兴奋的文章)。

+1

非常有趣的文章! – Sapph 2010-01-07 21:04:56

6

在我看来,你不能真正预见你的用户最终会使用它。因此,除非有一些内部行为你不希望用户瞎搞(即他们需要知道在你这样做之前必须先设置它,等等),否则通常不是一个好主意。

至于虚拟,你会倾向于做什么虚拟最终用户可能要重写。事件减少了对虚拟功能的需求,但仍然有时候你想让它们变成虚拟的。一般来说,您需要考虑给定成员函数如何可能需要由最终用户定制。

3

声明一个方法虚拟意味着某种契约:你的课程承认它可以被覆盖并预期。

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

密封恰恰相反,你可以说你不希望它被覆盖了。第二个原因可能是性能,但我不会太快。

2

有争议的观点:C# classes should be sealed by default

如果这个类不是被设计为继承的,而且你没有想过通过潜在的陷阱或记录如何正确继承,那么如果人们重写方法,你的类很可能会以奇怪的方式失败。如果你不封闭班级,你告诉班级的客户,你可以从班级中派生出来,你将来必须支持班级的这个“界面”,以避免打破选择从你的班级继承的客户。这会限制你将来如何修改你的班级。

因此,默认情况下封印类,除非您明确地要允许它。如果您确实想要允许它,请确保您记录哪些方法应通过虚拟方法被重写,以及重写的方法必须执行哪些操作以确保类继续工作(如调用基本方法)。

+1

我相信对于密封类也有很小的性能优势,因为运行时确实知道它不需要查找覆盖等,并且可以相应地进行优化。 – 2010-01-07 22:52:26

19

首先,我同意其他答案,建议积极密封任何并非专门设计用于扩展的东西。我不同意你需要一个理由来封印一些东西;相反,你需要一个理由让东西不被打开。

你的问题是非常笼统的,所以这里有一个普遍的答案:你的图书馆大概是特征的集合,它可以被用作解决用户问题的工具。你图书馆的每个功能都可能存在,因为你做了一些研究,发现有一个需要解决的用户问题,并为他们解决了这个问题。

这些功能中的每一个都有一定的相关成本。其中一些成本是过去的 - 您花在设计,实施,测试,调试和运输代码上的时间。其中一些成本尚未到来:维护,读取错误报告等等。还有更多微妙的成本,比如保持与现有功能之一的向后兼容性,将会增加明天实施新功能的成本。

可扩展性也是一个功能。这是一个功能,如果你弄错了,成本几乎完全在未来。像处理任何其他功能一样对待它:弄清楚它是否是用户真正需要的功能,以及它的好处是否会支付其成本。如果你不能清楚地评估可扩展性的好处或成本,那么它可能会不小心执行它。

+0

有用的方法来看看它,谢谢! – Sapph 2010-01-08 01:00:30

+0

+1“你需要一个理由离开东西” – Doval 2014-01-28 02:05:44

5

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

很有可能有一天比你更好的程序员会想要扩展你的课程,并且知道他们在做什么。我认为有很好的理由来封印是很少见的。

1

您有兴趣冲突这里。

  • 如果你想使它容易对您的呼叫,以测试他们的代码(例如做测试驱动开发),您的用户需要能够模拟类。
    • 所以所有的方法都必须是虚拟的,或者你必须有你实现的包含所有方法的接口。
  • 但是,如果您希望清楚说明您在用户可能创建的任何子类上提供的合同,那么您应该遵守Microsoft准则。
    • 不要让会员虚拟除非你有一个很好的理由这样做,你知道所有有关设计,测试和维护虚拟成员 的成本实在是可惜没有标记一个办法虚拟的方法来说,除了嘲笑之外,你不希望任何客户端代码实现它。