我应该标记所有虚方法吗?

在 Java 中,可以将方法标记为 final,以使其重写为 不可能

在 C # 中,必须将方法标记为 Virtual 才能使其重写为 有可能

这是否意味着在 C # 中应该标记所有虚方法(除了少数不希望被覆盖的方法) ,因为您很可能不知道您的类可以以何种方式继承?

9138 次浏览

不!因为您不知道如何继承您的类,所以如果您想要重写 知道,那么应该将 只有标记为 virtual

不。只有希望派生类指定的方法才应该是虚方法。

虚拟与最终无关。

为了防止重写 c # 中的虚方法,可以使用 sealed

public class MyClass
{
public sealed override void MyFinalMethod() {...}
}

在 C # 中,必须将方法标记为 Virtual 才能重写它。这是否意味着在 C # 中应该标记所有虚方法(除了少数不希望被覆盖的方法) ,因为您很可能不知道您的类可以以何种方式继承?

不。如果语言设计者认为虚拟应该是默认的,那么 那就是默认的

可重写性是一个 特写,和所有功能一样,它有 成本。可重写方法的成本是相当可观的: 设计、实现和测试成本都很高,特别是如果对类有任何“敏感性”的话; 虚方法是将未经测试的第三方代码引入系统的方法,这会对安全产生影响。

如果您不知道您打算如何继承您的类,那么 不要发布你的类,因为 你还没设计完呢。您的可扩展性模型肯定是您应该提前知道的东西; 它应该深刻地影响您的设计和测试策略。

我主张所有的类都是 密封,所有的方法都是 非虚拟的,直到您有一个真实的、以客户为中心的理由来解封或者使方法成为虚拟的。

基本上,您的问题是“我不知道我的客户打算如何使用我的类; 因此,我是否应该任意扩展它?”不,你应该 变得有知识!你不会问“我不知道我的客户将如何使用我的类,所以我应该让我的所有属性读写吗?”?我是否应该使我的所有方法都具有委托类型的读写属性,以便我的用户可以用他们自己的实现替换任何方法?”不,不要做任何这些事情,直到你有 证据,用户实际上需要该功能!把宝贵的时间花在设计、测试和实现用户真正想要和需要的特性上,并且从知识的角度出发。

我们可以想出任何一个阵营的理由,但这完全没有用。

在 Java 中有数百万个意外的非 final 公共方法,但是我们很少听到恐怖的故事。

在 C # 中有数百万个密封的公共方法,我们听到的恐怖故事非常少。

所以这没什么大不了的——重写一个公共方法的需要很少,所以无论如何都是没有意义的。


这让我想起了另一个参数——局部变量是否在默认情况下应该是 final。这是一个非常好的主意,但我们不能夸大它的价值。有数十亿的局部变量可能是,但不是,最终的,但它已被证明是一个实际问题。

在我看来,目前接受的 回答是不必要的教条。

事实上,当您不将一个方法标记为 virtual时,其他方法就不能覆盖它的行为,而当您将一个类标记为 sealed时,其他方法就不能从该类继承。这会造成巨大的痛苦。我不知道有多少次因为标记类 sealed或不标记方法 virtual而诅咒 API,仅仅因为它们没有预料到我的用例。

从理论上讲,只允许重写方法和继承类可能是正确的方法,这些方法和继承类本应该被重写和继承,但是在实践中,不可能预见每一个可能的场景,并且真的没有一个很好的理由被封闭起来。

  1. 如果你没有一个很好的理由,那么不要将类标记为 sealed.
  2. 如果您的库意味着要被其他人使用,那么至少尝试将包含 行为的类的主要方法标记为 virtual

进行调用的一种方法是查看方法或属性的名称。List 上的 GetLlength ()方法完全符合名称的含义,并且它不支持 解释的大部分内容。更改其实现可能不太透明,因此将其标记为 virtual可能是不必要的。标记为虚拟的 Add方法是更有用的,因为有人可以创建一个特殊的列表,只接受一些对象通过添加方法等。另一个示例是自定义控件。您可能希望使用主绘图方法 virtual,这样其他人就可以使用大部分的行为,只是改变外观,但是您可能不会覆盖 X 和 Y 属性。

最后,你通常不必马上做出决定。在一个内部项目,你可以轻松地改变代码无论如何,我不会担心这些事情。如果需要重写某个方法,总是可以在发生这种情况时使其成为虚方法。 相反,如果项目是一个由其他人使用并且更新缓慢的 API 或库,那么考虑哪些类和方法可能有用肯定是值得的。在这种情况下,我认为开放比严格封闭更好。

使方法虚拟化通常会降低需要调用它的任何代码的速度。这种减速将是微不足道的,但在某些情况下可能相当大(除其他外,因为非虚方法调用可能是内联的,这反过来可能允许优化器消除不必要的操作)。预测虚拟调用可能影响执行速度的程度并不总是可能的,除非这样做有明显的好处,否则通常应该避免做那些会使代码变慢的事情。

在许多情况下,使方法非虚拟化的性能好处可能足以证明方法在默认情况下是非虚拟的,但是当类被设计为可继承时,大多数方法应该是虚拟的和不密封的; 非虚拟或密封方法的主要用途应该是作为其他(可能受保护的)虚拟方法的包装器(想要改变底层行为的代码应该覆盖适当的虚拟而不是包装器)。

将类标记为 sealed或将继承限制为程序集中的其他类通常有与性能无关的原因。此外,如果一个类是外部可继承的,那么所有具有 protected作用域的成员都将有效地添加到其公共 API 中,而且对其行为的任何更改 在基础课上都可能破坏任何依赖于该行为的派生类。另一方面,如果一个类是可继承的,那么使其方法 virtual实际上并不会增加它的公开性。如果有什么不同的话,它可以减少派生类对基类内部的依赖,允许它们完全“隐藏”基类实现中与派生类不再相关的方面(例如,如果 List<T>的成员是虚拟的,一个覆盖了所有成员的派生类可以使用一个数组来存放东西(避免大对象堆问题) ,并且不必尝试保持 List<T>使用的私有数组与数组数组一致。

不,不应该将所有方法都标记为虚方法。您应该考虑如何继承类。如果类不应该被继承,那么标记它是密封的,显然成员不应该是虚拟的。如果您的类可能会被继承,那么您确实应该最大化重写该行为的能力。因此,除非您有理由不这样做,否则请慷慨地在此类中的任何地方使用虚拟。

是的,你应该。 我想回答一个与其他大多数答案不同的问题。 这是 C # 的一个缺陷,一个缺陷,一个设计上的错误。 您可以看到,与 Java 相比,其中所有方法都是“虚拟的”,除非, (「最终」)。 当然,如果有一个“矩形”类的方法是“面积”, 您希望拥有自己的类,该类表示带有边距的“矩形”。 你希望利用现有的类的所有属性和方法,你只是想添加一个属性的“边距”,增加一些价值的常规矩形区域,如果在矩形区域方法没有标记虚拟,你是注定的。 请给出一个方法,它接受一个矩形数组并返回所有矩形面积的和。 有些可以是规则的矩形,有些可以有边距。 现在读回标有“正确”的答案,描述为“安全问题”或“测试”。与无法控制的残疾相比,这些都毫无意义。

对于其他人回答“不”,我并不感到惊讶。 我很惊讶 C # 的作者在基于 Java 的语言上看不到这一点。