我应该将所有方法标记为虚拟吗?
本文关键字:虚拟 记为 有方法 我应该 | 更新日期: 2023-09-27 17:55:09
在Java中,您可以将方法标记为final,使其无法覆盖。
在 C# 中,必须将方法标记为虚拟才能重写。
这是否意味着在 C# 中您应该将所有方法标记为虚拟(除了一些您不想被覆盖的方法),因为很可能您不知道您的类可以通过什么方式继承?
在 C# 中,必须将方法标记为虚拟才能重写。这是否意味着在 C# 中您应该将所有方法标记为虚拟(除了一些您不想被覆盖的方法),因为很可能您不知道您的类可以通过什么方式继承?
不。如果语言设计者认为虚拟应该是默认的,那么它就是默认的。
可笑性是一项功能,与所有功能一样,它也有成本。可覆盖方法的成本相当可观:设计、实现和测试成本很高,特别是如果对类有任何"敏感性";虚拟方法是将未经测试的第三方代码引入系统的方法,并且会产生安全影响。
如果您不知道如何继承您的类,请不要发布您的类,因为您还没有完成设计它。您的可扩展性模型绝对是您应该提前知道的;它应该深刻影响您的设计和测试策略。
我主张所有类都是密封的,所有方法都是非虚拟的,直到你有一个以客户为中心的真实世界的理由来解封或使方法虚拟。
基本上你的问题是"我不知道我的客户打算如何消费我的课程;因此,我应该使它任意可扩展吗? 不;你应该变得有知识!您不会问"我不知道我的客户将如何使用我的类,那么我应该让我的所有属性都读写吗? 我是否应该使我的所有方法都成为委托类型的读写属性,以便我的用户可以用他们自己的实现替换任何方法? 不,在有证据表明用户确实需要该功能之前,不要执行任何这些操作! 将宝贵的时间花在设计、测试和实现用户真正想要和需要的功能上,并从知识的角度进行。
在我看来,目前接受的答案是不必要的教条。
事实是,当你不将一个方法标记为virtual
时,其他人不能覆盖它的行为,当你将一个类标记为sealed
其他人不能从该类继承。这可能会导致巨大的疼痛。我不知道有多少次我诅咒一个 API 来标记类sealed
或不标记方法virtual
仅仅是因为他们没有预料到我的用例。
从理论上讲,只允许重写方法和继承要重写和继承的类可能是正确的方法,但在实践中,不可能预见所有可能的情况,并且真的没有充分的理由如此封闭。
- 如果你没有很好的理由,那么不要将类标记为
sealed
. - 如果你的库是供其他人使用的,那么至少尝试将包含行为的类的主要方法标记为
virtual
。
进行调用的一种方法是查看方法或属性的名称。List 上的 GetLength() 方法完全符合名称的含义,并且不允许进行太多解释。更改其实现可能不是很透明,因此将其标记为virtual
可能是不必要的。将Add
方法标记为虚拟方法更有用,因为有人可以创建一个特殊的列表,该列表仅通过 Add 方法等接受某些对象。另一个示例是自定义控件。您可能希望使主绘制方法virtual
,以便其他人可以使用大部分行为并仅更改外观,但您可能不会覆盖 X 和 Y 属性。
最后,您通常不必立即做出决定。在一个内部项目中,无论如何你都可以轻松更改代码,我不会担心这些事情。如果需要重写某个方法,则在发生这种情况时,您始终可以将其设置为虚拟。相反,如果项目是一个被其他人使用并且更新缓慢的 API 或库,那么考虑哪些类和方法可能有用当然是值得的。在这种情况下,我认为最好是开放而不是严格封闭。
不!由于您不知道如何继承类,因此仅当知道要重写方法时才应将方法标记为virtual
。
No.只有希望派生类指定的方法才应该是虚拟的。
虚拟与最终无关。
若要防止重写 C# 中的虚拟方法,请使用sealed
public class MyClass
{
public sealed override void MyFinalMethod() {...}
}
是的,你应该这样做。我希望回答一个与大多数其他答案不同的答案。这是 C# 中的一个缺陷。一个缺陷。其设计错误。您可以看到,与所有方法都是"虚拟"的Java相比,除非,另有说明("最终")。当然,如果有一个带有"面积"方法的类"矩形",并且您希望拥有自己的类来表示带有边距的"矩形"。您希望利用现有类及其所有属性和方法,并且只想添加一个"margin"属性,该属性为常规矩形区域添加一些值,如果 Rectangle 中的 area 方法未标记为虚拟,则注定要失败。请图像 一个采用矩形数组并返回所有矩形面积之和的方法。有些可以是常规矩形,有些可以是边距。现在读回标记为"正确"的答案,描述"安全问题"或"测试"。与要覆盖的残疾相比,这些是毫无意义的。
其他人回答"不",我并不感到惊讶。我很惊讶C#的作者在基于Java的语言时看不到这一点。
我们可以想出任何一个阵营的理由,但这完全没用。
在Java中,有数百万个意想不到的非最终公共方法,但我们很少听到恐怖故事。
在 C# 中,有数以百万计的密封公共方法,我们很少听到恐怖故事。
所以这没什么大不了的 - 覆盖公共方法的需求很少见,所以无论哪种方式都没有意义。
这让我想起了另一个论点——默认情况下,局部变量是否应该是最终的。这是一个非常好的主意,但我们不能夸大它的价值。有数十亿个局部变量可能是,但不是最终的,但它已被证明是一个实际的问题。
使方法虚拟通常会减慢需要调用它的任何代码。 这种减速是微不足道的,但在某些情况下可能相当大(除其他外,因为非虚拟方法调用可能是内联的,这反过来可能允许优化程序消除不必要的操作)。 并不总是能够预测虚拟调用对执行速度的影响程度,并且通常应该避免做会使代码变慢的事情,除非这样做有明显的好处。
在许多情况下,使方法非虚拟的性能优势可能足以证明默认情况下方法为非虚拟的合理性,但是当类被设计为可继承时,大多数方法应该是虚拟的和未密封的;非虚拟或密封方法的主要用途应该是作为其他(可能受保护的)虚拟方法的包装器(想要更改基础行为的代码应该覆盖适当的虚拟而不是包装器)。
通常存在与性能无关的原因,将类标记为sealed
或将继承限制为程序集中的其他类。 除此之外,如果类是外部可继承的,则具有protected
作用域的所有成员都将有效地添加到其公共 API 中,并且基类中对其行为的任何更改都可能破坏依赖于该行为的任何派生类。 另一方面,如果一个类是可继承的,那么使其方法virtual
不会真正增加其曝光率。 如果有的话,它可以减少派生类对基类内部的依赖,允许它们完全"埋藏"基类实现中不再相关的派生类实现方面[例如,如果List<T>
的成员是虚拟的,则覆盖它们的派生类可以使用数组数组来保存东西(避免大型对象堆问题), 并且不必尝试使List<T>
使用的专用阵列与数组的数组保持一致。
不,您不应该将所有方法都标记为虚拟方法。 您应该考虑如何继承您的类。 如果类不应该被继承,那么将其标记为密封,显然成员不应该是虚拟的。 如果你的类很可能是继承的,你确实应该最大化重写行为的能力。因此,在此类类中,除非你有理由不这样做,否则请在此类类中慷慨地使用虚拟。