【发布时间】:2011-02-08 20:43:03
【问题描述】:
定义哪些方法不可覆盖而不是哪些是可覆盖的工作会少得多,因为(至少对我而言),当你设计一个类时,你不在乎如果它的继承人会覆盖你的方法......
那么,为什么 C# 中的方法不是自动虚拟的?这其中的常识是什么?
【问题讨论】:
-
我实际上希望编译器更进一步,并将您的类默认为密封,除非您特别想解封它。
定义哪些方法不可覆盖而不是哪些是可覆盖的工作会少得多,因为(至少对我而言),当你设计一个类时,你不在乎如果它的继承人会覆盖你的方法......
那么,为什么 C# 中的方法不是自动虚拟的?这其中的常识是什么?
【问题讨论】:
所以很清楚您是允许覆盖或方法还是强制隐藏方法(通过new关键字)。
强制您添加关键字可以消除可能存在的歧义。
【讨论】:
约定?仅此而已,我想。我知道 Java 会自动使方法成为虚拟方法,而 C# 不会,因此在某种程度上,对于什么更好,显然存在一些分歧。就个人而言,我更喜欢 C# 默认值 - 考虑到覆盖方法比 不 覆盖它们要少得多,因此显式定义虚拟方法似乎更简洁。
【讨论】:
您应该关心派生类中哪些成员可以被覆盖。
决定哪些方法要虚拟化应该是一个深思熟虑的决定 - 而不是自动发生的事情 - 与关于 API 的公共表面的任何其他决定一样。
【讨论】:
除了设计和清晰的原因之外,非虚拟方法在技术上也更好,原因如下:
因此,除非您有特定意图重写该方法,否则最好将其设为非虚拟方法。
【讨论】:
实际上,这是糟糕的设计实践。我的意思是,不关心哪些方法是可覆盖的,哪些不是。您应该始终考虑什么应该和应该是可覆盖的,就像您应该仔细考虑什么应该或不应该公开一样!
【讨论】:
我认为存在效率问题以及其他人发布的原因。如果方法不是虚拟的,则无需花费 cpu 周期来查找覆盖。
【讨论】:
当有人从您的类继承时,这将使他们能够在基类使用任何方法时更改其工作方式。如果您有一个方法,您绝对需要它在基类中以某种方式执行操作,那么您将无法不允许他人更改该功能。
这是一个例子。假设您有一个希望不返回错误的函数。有人进来并决定更改它,以便在星期二抛出一个超出范围的异常。现在基类中的代码失败了,因为它依赖于发生的事情发生了变化。
【讨论】:
因为它不是 Java
说真的,只是一种不同的支持理念。 Java 希望可扩展性是默认值,而封装是显式的,而 C# 希望可扩展性是显式的,封装是默认值。
【讨论】:
Anders Hejlsberg 在this interview 中回答了这个问题,我引用:
有几个原因。一个是 表现。我们可以观察到 人们用Java编写代码,他们忘记了 将他们的方法标记为最终方法。 因此,这些方法是虚拟的。 因为它们是虚拟的,所以它们不 表现也不错。有只是 与相关的性能开销 是一种虚拟方法。那是一个 问题。
一个更重要的问题是版本控制。 有两种思想流派 虚方法。学术学校 思想说:“一切都应该是 虚拟的,因为我可能想要 总有一天会推翻它。”务实的 思想流派,来自 构建可以运行的真实应用程序 现实世界说,“我们必须 非常小心我们所做的 虚拟的。”
当我们在 平台,我们做了很多 承诺它如何在 未来。对于非虚方法,我们 保证当你打电话给这个时 方法,x 和 y 会发生。什么时候我们 在 API 中发布虚拟方法,我们 不仅承诺当你打电话 这种方法,x 和 y 都会发生。我们 还承诺当你覆盖 此方法,我们将在此调用它 关于特定的顺序 这些其他的和国家将是 在这个和那个不变量中。
每次你在 API 中说虚拟时, 您正在创建一个回调挂钩。作为 操作系统或 API 框架设计师, 你必须非常小心 那。您不希望用户覆盖 并钩在任意点 一个 API,因为你不一定 做出那些承诺。人们可能 不完全理解他们的承诺 当他们做某事时正在做 虚拟的。
【讨论】:
另请参阅 A Conversation with Anders Hejlsberg, Part IV 的 Anders Hejlsberg(C# 的发明者)的回答。
【讨论】:
套用Eric Lippert,设计C#的人之一: 因此,当您从第三方收到的源代码发生更改时,您的代码不会意外损坏。也就是说,防止Brittle Base Class problem。
如果一个方法是虚拟的,如果你(假设)有意识地决定允许该函数是可替换的,并且围绕它进行设计、测试和记录。例如,如果您创建了一个函数“frob”,并且在某些后续版本中,基类的创建者决定也创建一个函数“frob”,会发生什么?
【讨论】:
当您想要指定您是允许或拒绝某事时,总是有两种方法。您可以信任所有人并惩罚罪人,也可以不信任所有人并强迫他们请求许可。
虚拟方法存在一些较小的性能问题 - 无法内联,调用速度比非虚拟方法慢 - 但这并不重要。
更重要的是,它们对您的设计构成威胁。这不是关心其他人会对你的班级做什么它是关于良好的对象设计。当一个方法是虚拟的时,你说你可以把它拔掉并用不同的实现来替换它。正如我所说,你必须将这种方法视为敌人——你不能相信它。你不能依赖任何副作用。您必须为该方法制定非常严格的合同并坚持下去。
如果您认为人类是非常懒惰和健忘的生物,哪种方法更谨慎?
我从未在我的设计中亲自使用过虚拟方法。如果我的类使用了一些逻辑并且我希望它可以互换,那么我只需为它创建接口。该接口构成上述合同。在某些情况下您确实需要虚拟方法,但我认为这种情况很少见。
【讨论】: