简体   繁体   English

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

[英]Why are private virtual methods illegal in C#?

Coming from a C++ background, this came as a surprise to me.来自 C++ 背景,这让我感到惊讶。 In C++ it's good practice to make virtual functions private.在 C++ 中,将虚拟函数设为私有是一种很好的做法。 From http://www.gotw.ca/publications/mill18.htm : "Guideline #2: Prefer to make virtual functions private."来自http://www.gotw.ca/publications/mill18.htm :“准则 #2:更喜欢将虚拟功能设为私有。”

I also quote Eric Lippert's blog, from Knights-knaves-protected-and-internal :我还引用了来自Knights-knaves-protected-and-internal的 Eric Lippert 的博客:

Private virtual methods are illegal in C#, which irks me to no end.私有虚拟方法在 C# 中是非法的,这让我很恼火。 I would totally use that feature if we had it.如果我们有这个功能,我会完全使用它。

I understand that in C#, you wouldn't be able to override a private virtual method in a derived (but not nested) class.我了解在 C# 中,您将无法在派生(但不是嵌套)class 中覆盖私有虚拟方法。 Why is this the case?为什么会这样? In C++ the access specifier has nothing to do with whether you can override a function or not.在 C++ 中,访问说明符与是否可以覆盖 function 无关。

I note that there are two questions here.我注意到这里有两个问题。 In the future you might consider posting two questions instead of combining two questions in one.将来您可能会考虑发布两个问题,而不是将两个问题合二为一。 When you combine questions like this often what happens is only the first one gets answered.当您将这样的问题结合起来时,通常只会出现第一个问题的答案。

The first question is "why are private virtual methods illegal in C#?"第一个问题是“为什么私有虚拟方法在 C# 中是非法的?”

Here are the arguments against the "private virtual methods" feature:以下是反对“私有虚拟方法”功能的论据:

  1. private virtual is only useful when you have a nested derived class. private virtual 仅在您具有嵌套派生类时才有用。 This is a useful pattern, but far less common than the non-nested derived class situation.这是一种有用的模式,但远不如非嵌套派生类情况常见。

  2. If you desire to restrict the ability to override the method in non-nested derived classes then you can do so by restricting the ability of non-nested classes to derive from the base class;如果您希望限制在非嵌套​​派生类中覆盖方法的能力,那么您可以通过限制非嵌套类从基类派生的能力来实现; make all the base class constructors private.将所有基类构造函数设为私有。 Therefore private virtual is not necessary to prevent overriding;因此,不需要私有虚拟来防止覆盖; protected virtual is sufficient, because the only derived classes will be nested. protected virtual 就足够了,因为只会嵌套派生类。

  3. If you desire to restrict the ability to call a method in a non-nested derived class then you can make the method internal virtual and then tell your coworkers to not use that method.如果您希望限制在非嵌套​​派生类中调用方法的能力,那么您可以将方法设为内部虚拟,然后告诉您的同事不要使用该方法。 It is irritating to have this not be enforced by the compiler, but the compiler does not enforce any other semantic constraint on how a method is supposed to be used either;编译器不强制执行这一点很令人恼火,但是编译器也没有对应该如何使用方法强制执行任何其他语义约束; getting the semantics right is your business, not the compiler's, and you have to enforce that with appropriate code reviews.获得正确的语义是你的事,而不是编译器的事,你必须通过适当的代码审查来强制执行。 Therefore private virtual is not necessary to prevent calling;因此,不需要私有虚拟来阻止调用; internal virtual plus code reviews is sufficient.内部虚拟加上代码审查就足够了。

  4. It is possible to implement this pattern already with existing parts:可以使用现有部件来实现此模式:

     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; } }

    Now I have a method F which is effectively virtual, but can only be "overridden" by derived nested classes.现在我有一个方法F是有效虚拟的,但由派生的嵌套类只能是“覆盖”。

Since in every case either protected, internal or protected internal does the trick, private virtual is unnecessary.由于在每种情况下受保护、内部或受保护的内部都可以解决问题,因此不需要私有虚拟。 It's almost never the right thing to do, since you have to be already committed to using the nested derived class pattern.这几乎从来都不是正确的做法,因为您必须已经承诺使用嵌套派生类模式。 So, the language makes it illegal.因此,该语言使其成为非法。

The arguments for are:的论据是:

There have been times in real code when I've want a virtual method to be a private implementation detail of a class that I want to be extended both by non-nested internal classes and nested internal classes.在实际代码中,有时我希望虚拟方法成为类的私有实现细节,我希望通过非嵌套内部类和嵌套内部类来扩展该类。 Having to enforce the invariant that the internal method not be called by my coworkers is vexing;必须强制执行不由我的同事调用内部方法的不变性是令人烦恼的; I'd like that to be enforced by the compiler without me having to jump through crazy hoops like making a field of delegate type, etc.我希望编译器能够强制执行这一点,而不必像制作委托类型的字段等那样疯狂地跳槽。

Also, there's simply the matter of consistency and orthogonality.此外,还有简单的一致性和正交性问题。 It seems weird that two things that ought to be independent -- accessibility and virtualness -- have an effect on each other unnecessarily.两个应该独立的东西——可访问性和虚拟性——却不必要地相互影响,这似乎很奇怪。

The arguments against the feature are pretty strong.反对该功能的论据非常强烈。 The arguments for are pretty weak.的论据非常薄弱。 Therefore, no such feature.因此,没有这样的功能。 I'd personally like it very much, but I totally understand why the design team has never taken me up on it.我个人非常喜欢它,但我完全理解为什么设计团队从未接受过我。 It's not worth the cost, and I would hate to not ship a better feature because we spent budget on a feature that benefits almost no one.这是不值得的成本,我不想发货更好的功能,因为我们上一个功能,收益几乎没有人花费预算。

The second question is "Why in C# are you not able to override a private virtual method in a derived non-nested class?"第二个问题是“为什么在 C# 中不能覆盖派生的非嵌套类中的私有虚拟方法?”

There are several reasons.有几个原因。

  1. Because you can only override what you can see.因为你只能覆盖你能看到的。 A private method is a private implementation detail of a base class and must not be accessible.私有方法是基类的私有实现细节,不能访问。

  2. Because allowing that has serious security implications.因为允许这样做会带来严重的安全隐患。 Remember, in C++ you almost always compile code into an application all at once.请记住,在 C++ 中,您几乎总是一次将代码编译到应用程序中。 You have the source code for everything;你拥有一切的源代码; everything is essentially "internal" from the C++ perspective most of the time.大多数时候,从 C++ 的角度来看,一切本质上都是“内部的”。 In C#, that's not at all the case.在 C# 中,情况完全不是这样。 Third party assemblies can easily get at public types from libraries and produce novel extensions to those classes which can then be used seamlessly in place of instances of the base class.第三方程序集可以轻松地从库中获取公共类型,并为这些类生成新的扩展,然后可以无缝地代替基类的实例使用这些扩展。 Since virtual methods effectively change the behaviour of a class, any code which depends for security reasons on invariants of that class needs to be carefully designed so that they do not depend on invariants guaranteed by the base class.由于虚方法有效地改变了类的行为,任何出于安全原因依赖于该类不变量的代码都需要仔细设计,以便它们不依赖于基类保证的不变量。 Restricting accessibility of virtual methods helps ensure that invariants of those methods are maintained.限制虚拟方法的可访问性有助于确保维护这些方法的不变量。

  3. Because allowing that provides another form of the brittle base class problem.因为允许它提供了另一种形式的脆弱基类问题。 C# has been carefully designed to be less susceptible to the brittle base class problem than other OO languages. C# 经过精心设计,与其他 OO 语言相比,不易受到脆弱基类问题的影响。 If an inaccessible virtual method could be overridden in a derived class then that private implementation detail of the base class becomes a breaking change if altered.如果一个不可访问的虚方法可以在派生类中被覆盖,那么基类的私有实现细节如果被改变就会变成一个破坏性的变化。 Providers of base classes should be free to change their internal details without worrying overmuch that they've broken derived classes which depend on them;基类的提供者应该可以自由地改变它们的内部细节,而不必担心他们破坏了依赖于它们的派生类; ideally only the public, documented interface to a type needs to be maintained when implementation details change.理想情况下,当实现细节发生变化时,只需要维护一个类型的公共、记录的接口。

Because private methods can ONLY be accessed from the class defining them, hence a private virtual method would be useless.因为私有方法只能从定义它们的类中访问,因此私有虚拟方法将是无用的。 What you want is a protected virtual method.您想要的是受保护的虚拟方法。 A protected method can be accessed by the class defining it and any subclasses.受保护的方法可以被定义它的类和任何子类访问。

EDIT:编辑:

By providing private and protected keywords C# allows you more granular control over your methods.通过提供私有和受保护的关键字,C# 允许您对方法进行更精细的控制。 That is private means completely closed and protected means completely closed apart from subclasses.那就是私有意味着完全封闭,而受保护意味着除了子类之外完全封闭。 This allows you to have methods that only your superclass knows about and methods that subclasses can know about.这允许您拥有只有您的超类知道的方法和子类可以知道的方法。

I would guess the reason is that internal virtual does almost the same thing as private virtual and is somewhat less confusing for those not familiar with the private virtual idiom.我猜原因是internal virtualprivate virtual几乎相同,并且对于那些不熟悉private virtual习语的人来说不太容易混淆。

Whereas only inner classes could override private virtual methods, only classess in your assembly can override internal virtual methods.而只有内部类可以覆盖private virtual方法,只有程序集中的类可以覆盖internal virtual方法。

In C# (and in the CLI, as far as i've seen), "private" has a pretty clear and unambiguous meaning: "accessible only in this class".在 C# 中(以及在 CLI 中,据我所知),“private”有一个非常明确和明确的含义:“只能在这个类中访问”。 The concept of private virtuals screws that all up, not to mention making namespaces a bit of a minefield.私有虚拟机的概念搞砸了,更不用说让命名空间成为雷区了。 Why should i have to care what you've called a method i can't even see, and get a compiler warning for happening to have chosen a name you already snagged for it?为什么我必须关心你调用了什么我什至看不到的方法,并因为碰巧选择了一个你已经为它而命名的名称而收到编译器警告?

Because C# does not have any mechanism for providing public/private/protected inheritance, which is what you are actually after.因为 C# 没有任何机制来提供公共/私有/受保护的继承,而这正是您所追求的。

Even in C++, private members cannot be accessed by derived classes, but they can limit the base class visibility by specifying inheritance visibility:即使在 C++ 中,派生类也不能访问私有成员,但它们可以通过指定继承可见性来限制基类的可见性:

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

C# provides a whole bunch of other things in order for you to control the visibility of your class' members. C# 提供了一大堆其他东西,以便您控制类成员的可见性。 Between protected and internal , you should be able to get the hierarchy exactly as you want.protectedinternal ,您应该能够完全按照您的意愿获得层次结构。

IMHO C# enforces stronger IS-A relationship via single base class inheritance, so it makes sense that if a car has an engine, a BMW subclass shouldn't be able to hide it.恕我直言,C# 通过单个基类继承强制执行更强的 IS-A 关系,因此如果汽车有引擎,BMW 子类不应该隐藏它是有道理的。

C++ supports multiple inheritance which is a less stricter IS-A relationship - it's almost like a HAS-A relationship where you can bring in multiple unrelated classes. C++ 支持多重继承,这是一种不太严格的 IS-A 关系——它几乎就像一个 HAS-A 关系,您可以在其中引入多个不相关的类。 Because of the ability to bring in multiple base classes, you want tighter control over the visibility of all of them.由于能够引入多个基类,您需要更严格地控​​制所有基类的可见性。

Let me make this clear: C# is not C++.让我澄清一下:C# 不是 C++。

C# is designed multiple decades after C++ and is built using advancing insights over the years. C# 是在 C++ 之后几十年设计的,并且是利用多年来的先进见解构建的。 In my humble opinion C# is well defined and finally handles object orientation the right way (imho).在我看来,C# 定义得很好,最终以正确的方式处理面向对象(恕我直言)。 It includes the internal statement for a reason and does not allow you to "virtualize" and override private methods.它出于某种原因包含internal语句,并且不允许您“虚拟化”和覆盖私有方法。 For a reason.因为某种原因。

All the issues described earlier (inner classes overriding private virtual methods, using the abstract factory pattern this way etc...) can be easily written in a different way using interfaces and the internal statement.前面描述的所有问题(内部类覆盖private virtual方法,以这种方式使用抽象工厂模式等等)都可以使用接口和internal语句以不同的方式轻松编写。 Having said that, I must say that it is fairly a matter of taste whether you like the C++ way or the C# way.话虽如此,我必须说,无论您喜欢 C++ 方式还是 C# 方式,这都是一个品味问题。

I prefer to use descriptive code (code that speaks for itself, without using comments) and I use interfaces instead of deep inheritance.我更喜欢使用描述性代码(代码不言自明,不使用注释),我使用接口而不是深度继承。 Overriding private virtual methods feels like hacking or spaghetti to me, regardless if it's common practice, an often used pattern or gets the job done.覆盖私有虚拟方法对我来说就像是黑客攻击或意大利面条,无论它是常见的做法、经常使用的模式还是完成工作。

I've been developing in C++ for almost 1.5 decades and I never came across the necessity for overriding private methods... ( I can see the comments flying in :-) )我已经用 C++ 开发了将近 1.5 年,但我从未遇到过覆盖私有方法的必要性......(我可以看到评论飞来飞去 :-))

Use:利用:

virtual private protected

Has the exact functionality of what you would expect a virtual private method to do.具有您期望虚拟私有方法执行的确切功能。 Can't be accessed outside the class but can be overriden by subclasses不能在 class 之外访问,但可以被子类覆盖

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM