为什么私人虚拟方法在C#中是非法的?

来自C ++的背景,这对我来说是一个惊喜。 在C ++中,使虚拟function保密是一个很好的做法。 来自http://www.gotw.ca/publications/mill18.htm :“指导方针2:倾向于将虚拟function私人化”。

我还引用了来自Knights-knaves-protected-and-internal的 Eric Lippert的博客:

私有虚拟方法在C#中是非法的,这让我非常沮丧。 如果有的话,我会完全使用这个function。

我明白,在C#中,您将无法重写派生(但不是嵌套)类中的私有虚拟方法。 这是为什么? 在C ++中,访问说明符与是否可以重写函数无关。

我注意到这里有两个问题。 将来您可能会考虑发布两个问题,而不是将两个问题合并为一个问题。 当你把这样的问题结合在一起的时候,往往会发生的只是第一个得到回答。

第一个问题是“为什么私人虚拟方法在C#中是非法的?

这里是对“私人虚拟方法”function的争论:

  1. 私有虚拟只有当你有一个嵌套的派生类时才有用。 这是一个有用的模式,但是比非嵌套派生类的情况要less得多。

  2. 如果您希望限制覆盖非嵌套派生类中方法的能力,那么可以通过限制非嵌套类从基类派生的能力来实现; 使所有的基类构造函数私有。 因此,私人虚拟是不必要的,以防止压倒一切; 受保护的虚拟就足够了,因为唯一的派生类将被嵌套。

  3. 如果您希望限制在非嵌套​​派生类中调用方法的能力,则可以使该方法内部为虚拟,然后告诉同事不要使用该方法。 编译器不能强制执行这个操作是令人烦恼的,但是编译器并没有对应该如何使用某个方法强加任何其他的语义约束。 让语义正确的是你的业务,而不是编译器,你必须通过适当的代码评论来执行。 因此,私人虚拟是不必要的,以防止呼叫; 内部虚拟加代码审查就足够了。

  4. 现有的部分已经可以实现这种模式:

    abstract class C { private int CF() { whatever; } private Func<int> f; public C() { f = CF; } private int F() { return f(); } private class D : C { private int DF() { whatever; } public D() { f = DF; } } 

    现在我有一个方法F,它是有效的虚拟,但只能由派生的嵌套类“重写”。

由于在任何情况下,受保护的,内部的或受保护的内部都有诀窍,私人虚拟是不必要的 这几乎从来都不是正确的事情,因为你必须已经致力于使用嵌套的派生类模式。 所以,这个语言是非法的。

争论的是:

当我想要一个虚拟的方法作为一个类的私有实现细节,我想通过非嵌套的内部类和嵌套的内部类进行扩展时,在实际代码中已经有很多次了。 不得不强调我的同事不能称之为内部方法的不变性是令人烦恼的; 我希望能够被编译器强制执行,而不必像编写委托types的字段一样跳过疯狂的循环。

另外,这只是一致性和正交性的问题。 似乎有点奇怪,应该是独立的两件事 – 可达性和虚拟性 – 不必要地相互影响。

反对这个function的争论非常强烈。 争论是相当薄弱的。 所以没有这个function。 我个人非常喜欢它,但是我完全理解为什么devise团队从来没有把我带到这里。 这是不值得的成本,我不希望发布一个更好的function,因为我们花了一个function几乎没有人受益的预算。

第二个问题是“为什么在C#中,你无法重写派生的非嵌套类中的私有虚拟方法?

有几个原因。

  1. 因为你只能重写你能看到的东西。 私有方法是基类的私有实现细节 ,不能被访问。

  2. 因为允许有严重的安全隐患。 请记住,在C ++中,您几乎总是一次将代码编译到应用程序中。 你有一切的源代码; 在大多数情况下,从C ++的angular度来看,一切都基本上是“内部”的。 在C#中,这并不是所有的情况。 第三方程序集可以很容易地从库中获取公共types,并为这些类生成新的扩展,然后可以无缝地使用这些扩展来代替基类的实例。 由于虚拟方法有效地改变了一个类的行为,任何依赖于该类的不variables的安全原因的代码都需要仔细devise,以便它们不依赖基类保证的不variables。 限制虚拟方法的可访问性有助于确保维护这些方法的不variables。

  3. 因为允许提供另一种forms的脆弱的基类问题。 C#经过精心devise,与其他OO语言相比,不易受到脆弱的基类问题的影响。 如果一个不可访问的虚拟方法可以在派生类中重写,那么基类的私有实现细节如果被改变就会变成一个重大改变。 基类的提供者应该可以自由地改变他们的内部细节,而不用担心他们已经打破了依赖于他们的派生类。 理想情况下,只有当实施细节发生变化时,需要维护一个types的公共接口。

因为私有方法只能从定义它们的类访问,所以私有的虚拟方法是没有用的。 你想要的是一个受保护的虚拟方法。 受保护的方法可以由定义它的类和任何子类访问。

编辑:

通过提供专用和受保护的关键字,C#可以让您更细化地控制您的方法。 这是私人的手段完全封闭和保护手段完全封闭分开的子类。 这可以让你拥有只有你的超类知道的方法和子类可以知道的方法。

我猜测原因是internal virtualprivate virtual 几乎是一样的,对于那些不熟悉private virtual习惯的人来说,这种混淆并没有那么混乱。

虽然只有内部类可以重写private virtual方法,但只有组件中的类可以重写internal virtual方法。

在C#中(以及在CLI中,据我所见),“私有”有一个非常清楚和明确的含义:“只有在这个类中才能访问”。 所有的私人虚拟的概念,更不用说使命名空间有点雷区。 为什么我不得不关心你所谓的方法,我什至不能看到,并得到一个编译器警告发生已经select了一个名字,你已经为它挂钩?

因为C#没有任何提供公共/私有/受保护的inheritance的机制,这就是你实际上所追求的。

即使在C ++中,私有成员也不能被派生类访问,但是它们可以通过指定inheritance可见性来限制基类的可见性:

 class Derived : /*->>*/private/*<--*/ Base { } 

C#提供了一大堆其他东西,以便您控制类的成员的可见性。 在protectedinternal ,您应该能够按照自己的意愿准确获取层次结构。

恕我直言,C#通过单一基类inheritance来强化IS-A关系,所以如果一辆汽车有一个引擎,宝马的一个子类就不应该隐藏起来。

C ++支持多重inheritance,这是一个不太严格的IS-A关系 – 它几乎就像一个HAS-A关系,你可以把多个不相关的类引入。 由于能够引入多个基类,所以您希望更严格地控​​制所有基类的可见性。

让我明确这一点:C#不是C ++。

C#是在C ++之后的几十年中devise出来的,并且使用多年来的先进洞察力来构build。 在我看来,C#已经被很好地定义了, 最终以正确的方式处理了对象的方向(imho)。 它包括internal陈述的原因,不允许你“虚拟化”和重写私有方法。 因为某种原因。

前面介绍的所有问题(内部类重写private virtual方法,使用抽象工厂模式等等)可以很容易地用接口和internal语句以不同的方式编写。 话虽如此,我必须说,无论您喜欢C ++还是C#方式,这都是一个相当有趣的问题。

我更喜欢使用描述性代码(代码本身,而不使用注释),我使用接口,而不是深度inheritance。 重写私有虚拟方法对我来说就像是黑客攻击或意大利面条,不pipe这是常见的做法,还是经常使用的模式或完成工作。

我已经用C ++进行了近十五年的开发,从来没有遇到过重写私有方法的必要性(我可以看到:-))