为什么私有虚拟方法在 C# 中是非法的?
对于具有 C++ 背景的我来说,这让我感到惊讶。在 C++ 中,将虚拟函数设为私有是一种很好的做法。来自 http://www.gotw.ca/publications/mill18.htm:“指南#2:更喜欢将虚拟函数设为私有。”
我还引用了 Eric Lippert 的博客,来自 骑士-knaves-protected-and-internal:
私有虚拟方法在 C# 中是非法的,这让我很恼火。我 如果我们有的话,我们会完全使用该功能。
据我了解,在 C# 中,您无法重写派生类(但不是嵌套类)中的私有虚拟方法。为什么会这样呢?在 C++ 中,访问说明符与是否可以重写函数无关。
Coming from a C++ background, this came as a surprise to me. In C++ it's good practice to make virtual functions private. From http://www.gotw.ca/publications/mill18.htm: "Guideline #2: Prefer to make virtual functions private."
I also quote Eric Lippert's blog, from Knights-knaves-protected-and-internal:
Private virtual methods are illegal in C#, which irks me to no end. 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. Why is this the case? In C++ the access specifier has nothing to do with whether you can override a function or not.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
在 C# 中(以及在 CLI 中,据我所知),“private”具有非常清晰且明确的含义:“只能在此类中访问”。私有虚拟的概念把一切都搞砸了,更不用说让命名空间变得有点雷区了。为什么我必须关心你所说的一个我什至看不到的方法,并因为碰巧选择了你已经为其获取的名称而收到编译器警告?
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". 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?
因为 C# 没有任何提供公共/私有/受保护继承的机制,而这正是您真正追求的。
即使在 C++ 中,派生类也无法访问私有成员,但它们可以通过指定继承可见性来限制基类可见性:
C# 提供了一大堆其他内容,以便您控制类成员的可见性。在
protected
和internal
之间,您应该能够准确地获得您想要的层次结构。恕我直言,C# 通过单个基类继承强制执行更强大的 IS-A 关系,因此如果汽车有引擎,BMW 子类不应该能够隐藏它,这是有道理的。
C++ 支持多重继承,这是一种不太严格的 IS-A 关系 - 它几乎就像 HAS-A 关系,您可以在其中引入多个不相关的类。由于能够引入多个基类,您希望更严格地控制所有基类的可见性。
Because C# does not have any mechanism for providing public/private/protected inheritance, which is what you are actually after.
Even in C++, private members cannot be accessed by derived classes, but they can limit the base class visibility by specifying inheritance visibility:
C# provides a whole bunch of other things in order for you to control the visibility of your class' members. Between
protected
andinternal
, you should be able to get the hierarchy exactly as you want.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++ 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. Because of the ability to bring in multiple base classes, you want tighter control over the visibility of all of them.
用途:
具有与您期望的虚拟私有方法完全相同的功能。
无法在类外部访问,但可以被子类覆盖
Use:
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
让我澄清一下:C# 不是 C++。
C# 的设计比 C++ 晚了几十年,并且是利用多年来先进的见解构建的。以我的拙见,C# 定义良好,并且终于以正确的方式处理面向对象(恕我直言)。它包含
internal
语句是有原因的,并且不允许您“虚拟化”和覆盖私有方法。这是有原因的。前面描述的所有问题(内部类重写
私有虚拟
方法,以这种方式使用抽象工厂模式等......)都可以使用接口和内部
以不同的方式轻松编写代码>声明。话虽如此,我必须说,无论您喜欢 C++ 方式还是 C# 方式,这都是一个品味问题。
我更喜欢使用描述性代码(不言而喻的代码,不使用注释)并且我使用接口而不是深度继承。对我来说,重写私有虚拟方法就像黑客攻击或意大利面条,无论它是常见做法、经常使用的模式还是完成工作。
我已经用 C++ 开发了近 1.5 年,但我从未遇到过重写私有方法的必要性......(我可以看到评论飞扬:-))
Let me make this clear: C# is not C++.
C# is designed multiple decades after C++ and is built using advancing insights over the years. In my humble opinion C# is well defined and finally handles object orientation the right way (imho). It includes the
internal
statement for a reason and does not allow you to "virtualize" and override private methods. 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 theinternal
statement.Having said that, I must say that it is fairly a matter of taste whether you like the C++ way or the C# way.
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# 中是非法的?”
以下是反对“私有虚拟方法”功能的论点:
私有虚拟方法仅在具有嵌套派生类时才有用。这是一种有用的模式,但远不如非嵌套派生类情况常见。
如果您希望限制覆盖非嵌套派生类中的方法的能力,则可以通过限制非嵌套类从基类派生的能力来实现;将所有基类构造函数设为私有。因此,私有虚拟没有必要来防止覆盖; protected virtual 就足够了,因为唯一的派生类将被嵌套。
如果您希望限制在非嵌套派生类中调用方法的能力,那么您可以将该方法设为内部虚拟,然后告诉您的同事不要使用该方法。编译器不强制执行这一点是令人恼火的,但编译器也不对如何使用方法强制执行任何其他语义约束;获得正确的语义是你的事,而不是编译器的事,你必须通过适当的代码审查来强制执行这一点。因此 private virtual 没有必要阻止调用;内部虚拟加上代码审查就足够了。
可以使用现有部件实现此模式:
现在我有一个方法 F,它实际上是虚拟的,但只能由派生嵌套类“覆盖”。
由于在每种情况下,受保护的、内部的或受保护的内部都可以达到目的,因此私有虚拟是不必要的。这几乎从来都不是正确的做法,因为您必须已经致力于使用嵌套派生类模式。因此,该语言使其非法。
支持的论点是:
在实际代码中,有时我希望虚拟方法成为我希望通过非嵌套内部类和嵌套内部类扩展的类的私有实现细节。必须强制执行不变量,即内部方法不被我的同事调用,这是令人烦恼的;我希望编译器能够强制执行这一点,而不必跳过疯狂的障碍,例如创建委托类型的字段等。
此外,还存在一致性和正交性的问题。两个本应独立的事物——可访问性和虚拟性——却不必要地相互影响,这似乎很奇怪。
反对该功能的争论非常激烈。其论据相当薄弱。因此,没有这样的功能。我个人非常喜欢它,但我完全理解为什么设计团队从来没有接受我。这不值得付出代价,而且我不想不推出更好的功能,因为我们将预算花在了一项几乎没有人受益的功能上。
第二个问题是“为什么在 C# 中无法重写派生非嵌套类中的私有虚拟方法?”
有几个原因。
因为你只能覆盖你能看到的内容。私有方法是基类的私有实现细节,并且不能被访问。
因为允许这样做会产生严重的安全隐患。请记住,在 C++ 中,您几乎总是一次性将代码编译到应用程序中。你拥有一切的源代码;大多数时候,从 C++ 的角度来看,一切本质上都是“内部”的。在 C# 中,情况完全不是这样。第三方程序集可以轻松地从库中获取公共类型,并对这些类生成新颖的扩展,然后可以无缝地使用这些扩展来代替基类的实例。由于虚拟方法有效地改变了类的行为,因此出于安全原因依赖于该类的不变量的任何代码都需要仔细设计,以便它们不依赖于基类保证的不变量。限制虚拟方法的可访问性有助于确保维护这些方法的不变性。
因为允许这会带来另一种形式的脆弱基类问题。 C# 经过精心设计,与其他 OO 语言相比,不易受到脆弱基类问题的影响。如果无法在派生类中重写不可访问的虚拟方法,则基类的私有实现细节一旦发生更改,就会成为重大更改。基类的提供者应该可以自由地更改其内部细节,而不必过分担心他们破坏了依赖于它们的派生类;理想情况下,当实现细节发生变化时,只需要维护类型的公共、记录的接口。
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#?"
Here are the arguments against the "private virtual methods" feature:
private virtual is only useful when you have a nested derived class. This is a useful pattern, but far less common than the non-nested derived class situation.
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.
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.
It is possible to implement this pattern already with existing parts:
Now I have a method F which is effectively virtual, but can only be "overridden" by derived nested classes.
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?"
There are several reasons.
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.
Because allowing that has serious security implications. Remember, in C++ you almost always compile code into an application all at once. You have the source code for everything; everything is essentially "internal" from the C++ perspective most of the time. In C#, that's not at all the case. 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.
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. 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.
由于私有方法只能从定义它们的类中访问,因此私有虚拟方法将毫无用处。您想要的是受保护的虚拟方法。受保护的方法可以由定义它的类和任何子类访问。
编辑:
通过提供私有和受保护的关键字,C# 允许您更精细地控制您的方法。即 private 意味着完全封闭,protected 意味着与子类完全封闭。这允许您拥有只有您的超类知道的方法和子类可以知道的方法。
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. 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.
我猜原因是
internal virtual
的作用几乎与private virtual
相同,并且对于那些不熟悉代码>私有虚拟
惯用语。虽然只有内部类可以重写
私有虚拟
方法,但只有程序集中的类可以重写内部虚拟
方法。I would guess the reason is that
internal virtual
does almost the same thing asprivate virtual
and is somewhat less confusing for those not familiar with theprivate virtual
idiom.Whereas only inner classes could override
private virtual
methods, only classess in your assembly can overrideinternal virtual
methods.