为什么 C# 默认将方法实现为非虚拟的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/814934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 02:15:02  来源:igfitidea点击:

Why C# implements methods as non-virtual by default?

c#java.netvirtual-functions

提问by Burcu Dogan

Unlike Java, why does C# treat methods as non-virtual functions by default? Is it more likely to be a performance issue rather than other possible outcomes?

与 Java 不同,为什么 C# 默认将方法视为非虚函数?它是否更有可能是性能问题而不是其他可能的结果?

I am reminded of reading a paragraph from Anders Hejlsberg about several advantages the existing architecture is bringing out. But, what about side effects? Is it really a good trade-off to have non-virtual methods by default?

我想起了安德斯·海尔斯伯格 (Anders Hejlsberg) 的一段关于现有架构带来的几个优势的段落。但是,副作用呢?默认情况下使用非虚拟方法真的是一个很好的权衡吗?

采纳答案by Mehrdad Afshari

Classes should be designedfor inheritance to be able to take advantage of it. Having methods virtualby default means that every function in the class can be plugged out and replaced by another, which is not really a good thing. Many people even believe that classes should have been sealedby default.

类应该为继承而设计,以便能够利用它。virtual默认情况下拥有方法意味着类中的每个函数都可以被插入并替换为另一个函数,这并不是一件好事。许多人甚至认为类应该是sealed默认的。

virtualmethods can also have a slight performance implication. This is not likely to be the primary reason, however.

virtual方法也可能有轻微的性能影响。然而,这不太可能是主要原因。

回答by Schildmeijer

C# is influenced by C++ (and more). C++ does not enable dynamic dispatch (virtual functions) by default. One (good?) argument for this is the question: "How often do you implement classes that are members of a class hiearchy?". Another reason to avoid enabling dynamic dispatch by default is the memory footprint. A class without a virtual pointer (vpointer) pointing to a virtual table, is ofcourse smaller than the corresponding class with late binding enabled.

C# 受 C++(以及更多)的影响。默认情况下,C++ 不启用动态调度(虚拟函数)。对此的一个(好?)论据是这样一个问题:“你多久实现一次作为类层次结构成员的类?”。避免默认启用动态调度的另一个原因是内存占用。没有指向虚拟表的虚拟指针(vpointer)的类当然比启用后期绑定的相应类小。

The performance issue is not so easy to say "yes" or "no" to. The reason for this is the Just In Time (JIT) compilation which is a run time optimization in C#.

性能问题不是那么容易说“是”或“否”的。这样做的原因是即时 (JIT) 编译,它是 C# 中的运行时优化。

Another, similar question about "speed of virtual calls.."

另一个关于“虚拟呼叫速度..”的类似问题

回答by Zifre

Because it's too easy to forget that a method may be overridden and not design for that. C# makes you think before you make it virtual. I think this is a great design decision. Some people (such as Jon Skeet) have even said that classes should be sealed by default.

因为很容易忘记一个方法可能会被覆盖而不是为此而设计。C# 让您先思考再虚拟化。我认为这是一个很棒的设计决定。有些人(比如 Jon Skeet)甚至说类应该默认是密封的。

回答by Trillian

To summarize what others said, there are a few reasons:

总结一下其他人所说的,有几个原因:

1-In C#, there are many things in syntax and semantics that come straight from C++. The fact that methods where not-virtual by default in C++ influenced C#.

1-在 C# 中,语法和语义中有很多东西直接来自 C++。事实上,C++ 中默认的非虚拟方法影响了 C#。

2-Having every method virtual by default is a performance concern because every method call must use the object's Virtual Table. Moreover, this strongly limits the Just-In-Time compiler's ability to inline methods and perform other kinds of optimization.

2-默认情况下让每个方法都虚拟是一个性能问题,因为每个方法调用都必须使用对象的虚拟表。此外,这严重限制了实时编译器内联方法和执行其他类型优化的能力。

3-Most importantly, if methods are not virtual by default, you can guarantee the behavior of your classes. When they are virtual by default, such as in Java, you can't even guarantee that a simple getter method will do as intended because it could be overridden to do anything in a derived class (of course you can, and should, make the method and/or the class final).

3-最重要的是,如果方法默认不是虚拟的,你可以保证你的类的行为。默认情况下,当它们是虚拟的时,例如在 Java 中,您甚至不能保证简单的 getter 方法会按预期执行,因为它可能会被覆盖以在派生类中执行任何操作(当然,您可以并且应该使方法和/或类final)。

One might wonder, as Zifre mentioned, why the C# language did not go a step further and make classes sealed by default. That's part of the whole debate about the problems of implementation inheritance, which is a very interesting topic.

正如 Zifre 所提到的,人们可能想知道,为什么 C# 语言没有更进一步,使类在默认情况下密封。这是关于实现继承问题的整个辩论的一部分,这是一个非常有趣的话题。

回答by JaredPar

The simple reason is design and maintenance cost in addition to performance costs. A virtual method has additional cost as compared with a non-virtual method because the designer of the class must plan for what happens when the method is overridden by another class. This has a big impact if you expect a particular method to update internal state or have a particular behavior. You now have to plan for what happens when a derived class changes that behavior. It's much harder to write reliable code in that situation.

原因很简单,除了性能成本之外,还有设计和维护成本。与非虚拟方法相比,虚拟方法具有额外的成本,因为类的设计者必须计划该方法被另一个类覆盖时会发生什么。如果您期望特定方法更新内部状态或具有特定行为,这会产生很大影响。您现在必须计划当派生类更改该行为时会发生什么。在这种情况下编写可靠的代码要困难得多。

With a non-virtual method you have total control. Anything that goes wrong is the fault of the original author. The code is much easier to reason about.

使用非虚拟方法,您可以完全控制。任何错误都是原作者的错。代码更容易推理。

回答by Tom Hawtin - tackline

It is certainly not a performance issue. Sun's Java interpreter uses he same code to dispatch (invokevirtualbytecode) and HotSpot generates exactly the same code whether finalor not. I believe all C# objects (but not structs) have virtual methods, so you are always going to need the vtbl/runtime class identification. C# is a dialect of "Java-like languages". To suggest it comes from C++ is not entirely honest.

这当然不是性能问题。Sun 的 Java 解释器使用相同的代码来分派(invokevirtual字节码),HotSpot 无论是否生成完全相同的代码final。我相信所有 C# 对象(但不是结构)都有虚拟方法,所以你总是需要vtbl/runtime 类标识。C# 是“类 Java 语言”的方言。暗示它来自 C++ 并不完全诚实。

There is an idea that you should "design for inheritance or else prohibit it". Which sounds like a great idea right up to the moment you have a severe business case to put in a quick fix. Perhaps inheriting from code that you don't control.

有一个想法,你应该“为继承而设计,否则就禁止它”。这听起来是个好主意,直​​到您有一个严重的商业案例需要快速解决。也许从您无法控制的代码继承。

回答by Thomas Bratt

If all C# methods were virtual then the vtbl would be much bigger.

如果所有 C# 方法都是虚拟的,那么 vtbl 会大得多。

C# objects only have virtual methods if the class has virtual methods defined. It is true that all objects have type information that includes a vtbl equivalent, but if no virtual methods are defined then only the base Object methods will be present.

如果类定义了虚拟方法,则 C# 对象只有虚拟方法。确实,所有对象都具有包含 vtbl 等效项的类型信息,但是如果没有定义虚拟方法,那么只会出现基本的 Object 方法。

@Tom Hawtin: It is probably more accurate to say that C++, C# and Java are all from the C family of languages :)

@Tom Hawtin:说 C++、C# 和 Java 都来自 C 语言家族可能更准确:)

回答by mwardm

I'm surprised that there seems to be such a consensus here that non-virtual-by-default is the right way to do things. I'm going to come down on the other - I think pragmatic - side of the fence.

我很惊讶这里似乎有这样的共识,即非默认默认是正确的做事方式。我将来到围栏的另一边——我认为是务实的——。

Most of the justifications read to me like the old "If we give you the power you might hurt yourself" argument. From programmers?!

大多数理由读给我听就像旧的“如果我们给你权力,你可能会伤害自己”的论点。从程序员?!

It seems to me like the coder who didn't know enough (or have enough time) to design their library for inheritance and/or extensibility is the coder who's produced exactly the library I'm likely to have to fix or tweak - exactly the library where the ability to override would come in most useful.

在我看来,那些不了解(或没有足够时间)来为继承和/或可扩展性设计他们的库的编码器正是编写了我可能必须修复或调整的库的编码器 - 正是覆盖能力最有用的库。

The number of times I've had to write ugly, desperate work-around code (or to abandon usage and roll my own alternative solution) because I can't override far, far outweighs the number of times I've ever been bitten (e.g. in Java) by overriding where the designer might not have considered I might.

我不得不编写丑陋、绝望的变通代码(或放弃使用并推出我自己的替代解决方案)的次数,因为我无法覆盖很远,远远超过我被咬过的次数(例如在 Java 中)通过覆盖设计师可能没有考虑过的地方。

Non-virtual-by-default makes my life harder.

非虚拟默认让我的生活更艰难。

UPDATE:It's been pointed out [quite correctly] that I didn't actually answer the question. So - and with apologies for being rather late....

更新:有人[完全正确]指出我实际上并没有回答这个问题。所以 - 并为迟到而道歉......

I kinda wanted to be able to write something pithy like "C# implements methods as non-virtual by default because a bad decision was made which valued programs more highly than programmers". (I think that could be somewhat justified based on some of the other answers to this question - like performance (premature optimisation, anyone?), or guaranteeing the behaviour of classes.)

我有点希望能够写一些简洁的东西,比如“默认情况下,C# 将方法实现为非虚拟的,因为做出了一个错误的决定,认为程序比程序员更重要”。(我认为基于对这个问题的其他一些答案,这可能在某种程度上是合理的——比如性能(过早优化,有人吗?),或保证类的行为。)

However, I realise I'd just be stating my opinion and not that definitive answer that Stack Overflow desires. Surely, I thought, at the highest level the definitive (but unhelpful) answer is:

但是,我意识到我只是在陈述我的观点,而不是 Stack Overflow 想要的明确答案。当然,我想,在最高级别上,确定的(但无益的)答案是:

They're non-virtual by default because the language-designers had a decision to make and that's what they chose.

默认情况下它们是非虚拟的,因为语言设计者必须做出决定,这就是他们的选择。

Now I guess the exact reason that they made that decision we'll never.... oh, wait! The transcript of a conversation!

现在我猜他们做出那个决定的确切原因我们永远不会......哦,等等! 对话记录!

So it would seem that the answers and comments here about the dangers of overriding APIs and the need to explicitly design for inheritance are on the right track but are all missing an important temporal aspect: Anders' main concern was about maintaining a class's or API's implicit contract across versions. And I think he's actually more concerned about allowing the .Net / C# platform to change under code rather than concerned about user-code changing on top of the platform. (And his "pragmatic" viewpoint is the exact opposite of mine because he's looking from the other side.)

因此,这里关于覆盖 API 的危险和显式设计继承的必要性的答案和评论似乎都在正确的轨道上,但都缺少一个重要的时间方面:Anders 的主要关注点是维护类或 API 的隐式跨版本合同。而且我认为他实际上更关心允许 .Net/C# 平台在代码下更改,而不是在平台上更改用户代码。(他的“务实”观点与我的完全相反,因为他是从另一边看的。)

(But couldn't they just have picked virtual-by-default and then peppered "final" through the codebase? Perhaps that's not quite the same.. and Anders is clearly smarter than me so I'm going to let it lie.)

(但他们不能只是选择默认虚拟,然后通过代码库添加“最终”?也许这不太一样......而且安德斯显然比我更聪明,所以我要让它撒谎。)

回答by Travis

Coming from a perl background I think C# sealed the doom of every developer who might have wanted to extend and modify the behaviour of a base class' thru a non virtual method without forcing all users of the new class to be aware of potentially behind the scene details.

来自 perl 背景,我认为 C# 注定了每个开发人员的厄运细节。

Consider the List class' Add method. What if a developer wanted to update one of several potential databases whenever a particular List is 'Added' to? If 'Add' had been virtual by default the developer could develop a 'BackedList' class that overrode the 'Add' method without forcing all client code to know it was a 'BackedList' instead of a regular 'List'. For all practical purposes the 'BackedList' can be viewed as just another 'List' from client code.

考虑 List 类的 Add 方法。如果开发人员想要在“添加”到特定列表时更新多个潜在数据库中的一个,该怎么办?如果默认情况下“Add”是虚拟的,开发人员可以开发一个“BackedList”类来覆盖“Add”方法,而无需强制所有客户端代码知道它是“BackedList”而不是常规的“List”。出于所有实际目的,“BackedList”可以被视为来自客户端代码的另一个“列表”。

This makes sense from the perspective of a large main class which might provide access to one or more list components which themselves are backed by one or more schemas in a database. Given that C# methods are not virtual by default, the list provided by the main class cannot be a simple IEnumerable or ICollection or even a List instance but must instead be advertised to the client as a 'BackedList' in order to ensure that the new version of the 'Add' operation is called to update the correct schema.

从可能提供对一个或多个列表组件的访问的大型主类的角度来看,这是有道理的,这些组件本身由数据库中的一个或多个模式支持。鉴于 C# 方法默认不是虚拟的,主类提供的列表不能是简单的 IEnumerable 或 ICollection 甚至是 List 实例,而是必须作为“BackedList”向客户端公布,以确保新版本调用“添加”操作以更新正确的架构。