在 Java 中,您可以将方法标记为 final 以使其无法覆盖。
在 C# 中,您必须将方法标记为虚拟方法才能覆盖。
这是否意味着在 C# 中您应该将所有方法标记为虚拟(除了一些您不想被覆盖的方法),因为您很可能不知道您的类可以以何种方式被继承?
在 Java 中,您可以将方法标记为 final 以使其无法覆盖。
在 C# 中,您必须将方法标记为虚拟方法才能覆盖。
这是否意味着在 C# 中您应该将所有方法标记为虚拟(除了一些您不想被覆盖的方法),因为您很可能不知道您的类可以以何种方式被继承?
在 C# 中,您必须将方法标记为虚拟方法才能覆盖。这是否意味着在 C# 中您应该将所有方法标记为虚拟(除了一些您不想被覆盖的方法),因为您很可能不知道您的类可以以何种方式被继承?
不,如果语言设计者认为 virtual 应该是默认值,那么它就是默认值。
可覆盖性是一种特性,并且与所有特性一样,它也有成本。可覆盖方法的成本相当高:设计、实现和测试成本很高,特别是如果对类有任何“敏感性”;虚拟方法是将未经测试的第三方代码引入系统并具有安全影响的方法。
如果你不知道你打算如何继承你的类,那么不要发布你的类,因为你还没有完成设计它。您的可扩展性模型绝对是您应该提前知道的;它应该深刻地影响您的设计和测试策略。
我主张所有类都是密封的,所有方法都是非虚拟的,直到你有一个以客户为中心的真实理由来解封或使方法成为虚拟。
基本上你的问题是“我不知道我的客户打算如何消费我的课程;因此我应该让它任意扩展吗?” 不; 你应该变得知识渊博!你不会问“我不知道我的客户将如何使用我的类,所以我应该让我的所有属性都可以读写吗?我应该让我的所有方法都可以读写委托类型的属性,以便我的用户可以用自己的实现替换任何方法吗?” 不,在您有证据表明用户确实需要该功能之前,不要做任何这些事情!花宝贵的时间设计、测试和实现用户真正想要和需要的功能,并从知识的角度来做。
在我看来,目前接受的答案是不必要的教条主义。
事实是,当您不将方法标记为 时virtual
,其他人无法覆盖其行为,并且当您将类标记为sealed
其他人无法从该类继承时。这可能会导致严重的疼痛。我不知道有多少次我诅咒一个用于标记类sealed
或不标记方法的 API,virtual
只是因为它们没有预料到我的用例。
从理论上讲,仅允许重写方法和继承本应被重写和继承的类可能是正确的方法,但实际上不可能预见到所有可能的情况,而且确实没有充分的理由如此封闭。
sealed
.virtual
.进行调用的一种方法是查看方法或属性的名称。List 上的GetLength()方法完全符合其名称的含义,并且不允许进行太多解释。更改其实现可能不是很透明,因此virtual
可能没有必要将其标记为。将方法标记Add
为虚拟更有用,因为有人可以创建一个特殊的列表,它只通过 Add 方法等接受一些对象。另一个例子是自定义控件。您可能希望创建主要绘图方法virtual
,以便其他人可以使用大部分行为并更改外观,但您可能不会覆盖 X 和 Y 属性。
最后,您通常不必立即做出决定。在一个内部项目中,无论如何您都可以轻松更改代码,我不会担心这些事情。如果一个方法需要被覆盖,你可以在发生这种情况时将其设为虚拟。相反,如果项目是一个被其他人使用并且更新缓慢的 API 或库,那么考虑哪些类和方法可能有用肯定是值得的。在这种情况下,我认为最好是开放而不是严格封闭。
不!因为您不知道您的类将如何被继承,所以您应该只标记一个方法,就virtual
好像您知道您希望它被覆盖一样。
不。只有您希望派生类指定的方法应该是虚拟的。
虚拟与最终无关。
为了防止在 c# 中覆盖虚拟方法,您可以使用sealed
public class MyClass
{
public sealed override void MyFinalMethod() {...}
}
我们可以想出支持/再次支持任一阵营的理由,但这完全没用。
在 Java 中有数以百万计的非最终公共方法,但我们听到的恐怖故事很少。
在 C# 中,有数百万个密封的公共方法,我们听到的恐怖故事很少。
所以这没什么大不了的 - 覆盖公共方法的需要很少见,所以无论哪种方式都没有实际意义。
这让我想起了另一个论点——一个局部变量是否应该是默认的。这是个好主意,但我们不能夸大它的价值。有数十亿个局部变量可能是但不是最终的,但它已被证明是一个实际问题。
Yes you should. I wish to answer a different answer than most other answers. This is a flaw in C#. A defect. A mistake in its design. You can see that when comparing to Java where all methods are "virtual" unless, specified otherwise ("final"). Of course that if there is a class "Rectangle" with a method of "Area", and you wish to have your own class that represents a "Rectangle" with margins. You wish to take advantage of the existing class with all of its properties and methods and you just want to add a property of "margin" that adds some value to the regular rectangle area, and if the area method in Rectangle is not marked virtual, you are doomed. Image please a method that takes an array of Rectangles and returns the sum of the area of all rectangles. Some can be regular rectangles and some with margin. Now read back the answer that is marked "correct" the describe "secuirty issue" or "testing". Those are meaningless compared to the disability to override.
I am not surprised that others answered "no". I am surprised that the authors of C# couldn't see that while basing their language on Java.
使方法虚拟化通常会减慢任何需要调用它的代码。这种减速将是微不足道的,但在某些情况下可能会相当大(除其他外,因为非虚拟方法调用可能是内联的,这反过来可能允许优化器消除不必要的操作)。预测虚拟调用可能影响执行速度的程度并不总是可能的,并且通常应该避免做会使代码变慢的事情,除非这样做有明显的好处。
在许多情况下,使方法成为非虚拟方法的性能优势可能足以证明默认情况下方法是非虚拟的,但是当类被设计为被继承时,大多数方法应该是虚拟的并且是非密封的;非虚拟或密封方法的主要用途应该是作为其他(可能受保护的)虚拟方法的包装器(想要更改底层行为的代码应该覆盖适当的虚拟而不是包装器)。
sealed
将类标记为或限制对程序集中其他类的继承通常有与性能无关的原因。除此之外,如果一个类是可外部继承的,那么所有具有作用域的成员都会有效地添加到它的公共 API 中,并且在基类protected
中对其行为的任何更改都可能会破坏任何依赖该行为的派生类。另一方面,如果一个类是可继承的,那么创建它的方法并不会真正增加它的曝光率。如果有的话,它可以减少派生类对基类内部的依赖,方法是允许派生类完全“隐藏”派生类中不再相关的基类实现的方面[例如,如果virtual
List<T>
是虚拟的,一个覆盖它们的派生类可以使用数组数组来保存东西(避免大对象堆问题),并且不必尝试保持使用的私有数组List<T>
与数组一致数组。
不,您不应该将所有方法都标记为虚拟方法。您应该考虑如何继承您的类。如果该类不应该被继承,那么将其标记为密封的,显然成员不应该是虚拟的。如果你的类很可能被继承,你真的应该最大化覆盖行为的能力。因此,除非您有理由不这样做,否则请在此类课程中到处使用 virtual。