【问题标题】:Why methods in C# are not automatically virtual? [duplicate]为什么 C# 中的方法不是自动虚拟的? [复制]
【发布时间】:2011-02-08 20:43:03
【问题描述】:

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

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

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

【问题讨论】:

标签: c# virtual


【解决方案1】:

所以很清楚您是允许覆盖或方法还是强制隐藏方法(通过new关键字)。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      决定哪些方法要虚拟化应该是一个深思熟虑的决定 - 而不是自动发生的事情 - 与关于 API 的公共表面的任何其他决定一样。

      【讨论】:

      • 我不再输入我的答案,因为它本质上是一个更罗嗦的版本。 +1。
      • Java 人显然不同意你的观点……虽然我想我会的。
      • +1 是的,一旦你做了一些虚拟的东西,在考虑你的类将如何被其他人使用时,你需要考虑更多的场景,这会增加设计、实现和测试的复杂性。
      • @Matt:如果您的课程设计精良且简单明了,那么没有什么可以阻止您将所有成员虚拟化。默认为非虚拟仅有助于降低整个类层次结构因设计不良的基类中的更改而被破坏的风险。
      • @Luke:允许,但默认情况下将其设为虚拟会有一些副作用。首先,运行时需要非常擅长对它们进行优化(您会看到虚拟和异常。在 java 中,它们很快,因为它们需要,在 C# 中它们都非常昂贵)。其次,一旦您开始谈论模拟(或使用动态代理做任何事情),您的选择就会变得更加有限,因为人们只会在他们想到时将方法标记为虚拟,即使方法是虚拟的也不会'没有负面影响。
      【解决方案4】:

      除了设计和清晰的原因之外,非虚拟方法在技术上也更好,原因如下:

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

      因此,除非您有特定意图重写该方法,否则最好将其设为非虚拟方法。

      【讨论】:

      • 如果 JIT 可以检测到虚拟方法没有在任何地方被覆盖,则可以内联虚拟方法。让 JIT 根据方法的运行时使用来选择是否进行内联是两全其美的。
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

          当有人从您的类继承时,这将使他们能够在基类使用任何方法时更改其工作方式。如果您有一个方法,您绝对需要它在基类中以某种方式执行操作,那么您将无法不允许他人更改该功能。

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

          【讨论】:

            【解决方案8】:

            因为它不是 Java

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

            【讨论】:

              【解决方案9】:

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

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

              一个更重要的问题是版本控制。 有两种思想流派 虚方法。学术学校 思想说:“一切都应该是 虚拟的,因为我可能想要 总有一天会推翻它。”务实的 思想流派,来自 构建可以运行的真实应用程序 现实世界说,“我们必须 非常小心我们所做的 虚拟的。”

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

              每次你在 API 中说虚拟时, 您正在创建一个回调挂钩。作为 操作系统或 API 框架设计师, 你必须非常小心 那。您不希望用户覆盖 并钩在任意点 一个 API,因为你不一定 做出那些承诺。人们可能 不完全理解他们的承诺 当他们做某事时正在做 虚拟的。

              【讨论】:

              • 是的,安德斯说的。这并不能使他正确。我觉得他错了。 :)
              【解决方案10】:

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

              【讨论】:

              • +1:这是一次很棒的采访!
              【解决方案11】:

              套用Eric Lippert,设计C#的人之一: 因此,当您从第三方收到的源代码发生更改时,您的代码不会意外损坏。也就是说,防止Brittle Base Class problem

              如果一个方法是虚拟的,如果你(假设)有意识地决定允许该函数是可替换的,并且围绕它进行设计、测试和记录。例如,如果您创建了一个函数“frob”,并且在某些后续版本中,基类的创建者决定也创建一个函数“frob”,会发生什么?

              【讨论】:

                【解决方案12】:

                当您想要指定您是允许或拒绝某事时,总是有两种方法。您可以信任所有人并惩罚罪人,也可以不信任所有人并强迫他们请求许可。

                虚拟方法存在一些较小的性能问题 - 无法内联,调用速度比非虚拟方法慢 - 但这并不重要。

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

                如果您认为人类是非常懒惰和健忘的生物,哪种方法更谨慎

                从未在我的设计中亲自使用过虚拟方法。如果我的类使用了一些逻辑并且我希望它可以互换,那么我只需为它创建接口。该接口构成上述合同。在某些情况下您确实需要虚拟方法,但我认为这种情况很少见。

                【讨论】:

                • 从未使用过虚拟方法?喜欢,从来没有?
                猜你喜欢
                • 2020-09-07
                • 2012-09-23
                • 2010-11-01
                • 1970-01-01
                • 1970-01-01
                • 2019-02-25
                • 1970-01-01
                • 1970-01-01
                • 2014-03-31
                相关资源
                最近更新 更多