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

Jon*_*Jon 39 c# virtual private

来自C++背景,这让我感到惊讶.在C++中,将虚拟函数设为私有是一种很好的做法.来自http://www.gotw.ca/publications/mill18.htm:"准则#2:更喜欢将虚拟功能设为私有."

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

私有虚拟方法在C#中是非法的,这让我感到厌烦.如果我们拥有它,我会完全使用该功能.

据我所知,在C#中,您无法覆盖派生(但不是嵌套)类中的私有虚方法.为什么会这样?在C++中,访问说明符与是否可以覆盖函数无关.

Eri*_*ert 38

我注意到这里有两个问题.在将来,您可以考虑发布两个问题,而不是将两个问题合二为一.当你结合这样的问题时,经常发生的事情只有第一个得到回答.

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

以下是针对"私有虚拟方法"功能的参数:

  1. private virtual仅在您拥有嵌套派生类时才有用.这是一个有用的模式,但远不如非嵌套的派生类情况.

  2. 如果您希望限制在非嵌套派生类中重写方法的能力,那么您可以通过限制非嵌套类从基类派生的能力来实现; 使所有基类构造函数都是私有的.因此私人虚拟不一定要防止覆盖; protected virtual就足够了,因为只有派生类才会嵌套.

  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; }
        }
    
    Run Code Online (Sandbox Code Playgroud)

    现在我有一个方法F是有效虚拟的,但由派生的嵌套类只能是"覆盖".

因为在每种情况下,受保护的,内部的或受保护的内部都可以实现这一目 这几乎从来都不是正确的事情,因为你必须已经承诺使用嵌套的派生类模式.所以,这种语言使它成为非法.

论证是:

在实际代码中,我曾希望虚拟方法成为类的私有实现细节,我希望通过非嵌套内部类和嵌套内部类来扩展它.必须强制执行内部方法不被我的同事调用的不变量是令人烦恼的; 我希望编译器能够强制执行它,而不必像在制作委托类型的字段时那样跳过疯狂的箍.

此外,还有一致性和正交性问题.似乎很奇怪,应该独立的两件事 - 可访问性和虚拟性 - 不必要地相互影响.

反对该功能的论据非常强烈.争论非常弱.因此,没有这样的功能.我个人非常喜欢它,但我完全理解为什么设计团队从来没有接受过我.这是不值得的成本,我不想出货更好的功能,因为我们的互利共赢几乎没有一个功能花费预算.

第二个问题是"为什么在C#中你不能覆盖派生的非嵌套类中的私有虚方法?"

有几个原因.

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

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

  3. 因为允许那提供另一种形式的脆弱基类问题.C#经过精心设计,比其他OO语言更不容易受到脆弱的基类问题的影响.如果可以在派生类中重写不可访问的虚方法,则基类的私有实现细节如果被更改则成为重大更改.基类的提供者应该可以自由地改变他们的内部细节,而不必担心他们已经破坏了依赖于它们的派生类; 理想情况下,只有在实现细节发生变化时,才需要维护类型的公共文档化接口.

  • 允许派生类覆盖私有虚拟成员但不调用它除了通过链到`base`将允许基类确保对方法的所有调用都包含在基本提供的代码中.例如,`private virtual doPerformAction(){...}; public doPerformAction {lock(myThing){doPerformAction(); }`.由于这不起作用,有没有其他方法来强制执行该模式? (2认同)

Dar*_*o Z 15

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

编辑:

通过提供私有和受保护的关键字,C#允许您对方法进行更精细的控制.这是私有意味着完全关闭,受保护意味着完全关闭,而不是子类.这允许您拥有只有您的超类知道的方法以及子类可以知道的方法.

  • `private virtual`仍然可以从内部类访问(阅读Eric的博客上的整个帖子进行更长时间的讨论).如果要创建类型层次结构并希望仅将继承限制为您了解/控制的类,则它们非常有用.说实话,我的想法是他们没有包含它,因为"内部虚拟"几乎*做同样的事情. (12认同)

Dea*_*ing 5

我猜想原因是internal virtual几乎private virtual那些不熟悉这个private virtual习语的人做同样的事情并且不那么令人困惑。

而只有内部类可以覆盖private virtual方法,只有程序集中的类可以覆盖internal virtual方法。