为什么C#中的方法不是自动虚拟的?

时间:2010-04-16 14:20:31

标签: c# virtual

  

可能重复:
  Why C# implements methods as non-virtual by default?

定义哪些方法不可忽略而不是 可以覆盖哪些方法要少得多,因为(至少对我而言),当你设计一个类时,你并不在乎如果它的继承人会覆盖你的方法......

那么,为什么C#中的方法不是自动虚拟的?这是什么常识?

12 个答案:

答案 0 :(得分:25)

Anders Hejlsberg在this interview回答了这个问题,我引用了:

  

有几个原因。一个是   性能。我们可以观察到这一点   人们用Java编写代码,他们忘了   最终标记他们的方法。   因此,这些方法是虚拟的。   因为它们是虚拟的,所以它们不是   表现也很好。只是   与...相关的性能开销   是一种虚拟方法。那是一个   问题。

     

更重要的问题是版本控制。   有两种思想流派   虚拟方法。学院   思想说:“一切都应该如此   虚拟,因为我可能想要   有一天会超越它。“务实   思想学派,来自   构建运行的实际应用程序   现实世界说,“我们必须成为现实世界   真正小心我们的所作所为   虚拟“。

     

当我们制作虚拟的东西时   平台,我们做了很多   关于它如何演变的承诺   未来。对于非虚方法,我们   承诺,当你打电话给这个   方法,x和y会发生。什么时候我们   我们在API中发布虚拟方法   不仅在你打电话时承诺   这种方法,x和y会发生。我们   当你覆盖时也承诺   这个方法,我们将在此调用它   关于的特定顺序   这些其他的和国家将是   在这个和那个不变的。

     

每当你在API中说虚拟时,   你正在创建一个回拨钩子。如   OS或API框架设计器,   你必须要真正小心   那。您不希望用户覆盖   并在任何任意点挂钩   一个API,因为你不一定   做出这些承诺。人们可能会   不完全明白他们的承诺   正在制作什么东西   虚拟

答案 1 :(得分:20)

您应该关心在派生类中可以覆盖哪些成员。

决定哪些虚拟方法应该是经过深思熟虑的深思熟虑的决定 - 而不是自动发生的事情 - 与关于API公共表面的任何其他决策相同。

答案 2 :(得分:6)

除了设计和清晰度原因之外,非虚拟方法在技术上也更好,原因有两个:

  • 虚拟方法需要更长时间才能调用(因为运行时需要浏览虚拟查找表以找到要调用的实际方法)
  • 无法内联虚拟方法(因为编译器在编译时不知道最终会调用哪个方法)

因此,除非您有特定的意图覆盖该方法,否则最好是非虚拟的。

答案 3 :(得分:5)

公约?没什么,我想。我知道Java自动使方法成为虚拟方法,而C#则不然,所以在某种程度上显然存在一些关于什么更好的分歧。就个人而言,我更喜欢C#默认 - 考虑重写方法比覆盖它们要少得多,因此显式定义虚方法似乎更简洁。

答案 4 :(得分:3)

另见A Conversation with Anders Hejlsberg, Part IV的Anders Hejlsberg(C#的发明者)的回答。

答案 5 :(得分:3)

解释Eric Lippert,其中一位设计C#的人: 因此,当您从第三方收到的源代码发生更改时,您的代码不会被意外损坏。换句话说,要阻止Brittle Base Class problem

如果一个方法是虚拟的,如果因为你(据说)做出了有意识的决定,允许该功能可以替换,并设计,测试和记录。例如,如果你创建了一个函数“frob”会发生什么,并且在某个后续版本中,基类的制作者决定也创建一个“frob”函数?

答案 6 :(得分:2)

很明显,您是否允许覆盖或方法或强制隐藏方法(通过new关键字)。

强制您添加关键字可以消除可能存在的任何歧义。

答案 7 :(得分:2)

当您想要指定允许或拒绝某些内容时,总会有两种方法。你既可以信任每个人,也可以惩罚罪人,或者你可以不信任所有人并迫使他们征得许可。

虚拟方法存在一些次要性能问题 - 无法内联,调用速度比非虚拟方法慢 - 但实际上并不重要。

更重要的是,它们会对您的设计构成威胁。这不是关心别人会对你的课程做些什么它是关于良好的对象设计。当一个方法是虚拟的时,你说你可以将其插出并用不同的实现替换它。正如我所说,你必须将这种方法视为敌人 - 你不能相信它。你不能依赖任何副作用。你必须为这种方法设立非常严格的合同并坚持下去。

如果你认为人是非常懒惰而忘记了生物哪种方法更谨慎

我的设计中有从不个人使用的虚拟方法。如果我的类使用了一些逻辑,并且我希望它可以互换,那么我只是为它创建接口。该界面构成上述合同。在某些情况下,您确实需要虚拟方法,但我认为这些非常罕见。

答案 8 :(得分:1)

我认为存在效率问题以及其他人发布的原因。如果方法不是虚拟的,则无需花费cpu周期来查找覆盖。

答案 9 :(得分:1)

当某人从您的类继承时,这将使他们能够在基类使用它时更改任何方法的工作方式。如果你有一个方法,你绝对需要它在基类中以某种方式执行一个动作,你就没有办法让别人改变那个功能。

这是一个例子。假设您有一个预期不会返回错误的函数。有人进来并决定更改它,以便在星期二,它会抛出超出范围的异常。现在基类中的代码失败了,因为它依赖于发生的事情发生了变化。

答案 10 :(得分:1)

因为它不是Java

说真的,只是一种不同的支持理念。 Java希望扩展性是默认的,封装是显式的,C#需要可扩展性是显式的,封装是默认的。

答案 11 :(得分:0)

实际上,这是糟糕的设计实践。关心哪些方法是可以覆盖的,哪些不是,我的意思是。您应该始终考虑应该和应该覆盖的内容,就像您应该仔细考虑应该或不应该公开的内容一样!