覆盖方法的 C# 可选参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8909811/
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
C# optional parameters on overridden methods
提问by SARI
Seems like in .NET Framework there is an issue with optional parameters when you override the method. The output of the code below is: "bbb" "aaa" . But the output I'm expecting is: "bbb" "bbb" .Is there a solution for this. I know it can be solved with method overloading but wondering the reason for this. Also the code works fine in Mono.
似乎在 .NET Framework 中,当您覆盖该方法时,可选参数存在问题。下面代码的输出是: "bbb" "aaa" 。但我期待的输出是: "bbb" "bbb" 。是否有解决方案。我知道它可以通过方法重载来解决,但想知道这样做的原因。该代码在 Mono 中也能正常工作。
class Program
{
class AAA
{
public virtual void MyMethod(string s = "aaa")
{
Console.WriteLine(s);
}
public virtual void MyMethod2()
{
MyMethod();
}
}
class BBB : AAA
{
public override void MyMethod(string s = "bbb")
{
base.MyMethod(s);
}
public override void MyMethod2()
{
MyMethod();
}
}
static void Main(string[] args)
{
BBB asd = new BBB();
asd.MyMethod();
asd.MyMethod2();
}
}
采纳答案by Jon Hanna
One thing worth noting here, is that the overridden version is called each time. Change the override to:
这里值得注意的一件事是,每次都会调用覆盖的版本。将覆盖更改为:
public override void MyMethod(string s = "bbb")
{
Console.Write("derived: ");
base.MyMethod(s);
}
And the output is:
输出是:
derived: bbb
derived: aaa
A method in a class can do one or two of the following:
类中的方法可以执行以下一项或两项操作:
- It defines an interface for other code to call.
- It defines an implementation to execute when called.
- 它定义了一个供其他代码调用的接口。
- 它定义了一个在调用时执行的实现。
It may not do both, as an abstract method does only the former.
它可能不会两者都做,因为抽象方法只做前者。
Within BBBthe call MyMethod()calls a method definedin AAA.
在BBB调用MyMethod()调用方法定义在AAA。
Because there is an override in BBB, calling that method results in an implementation in BBBbeing called.
因为在 中有一个覆盖BBB,调用该方法会导致调用一个实现BBB。
Now, the definition in AAAinforms calling code of two things (well, a few others too that don't matter here).
现在,中的定义AAA通知调用代码两件事(好吧,还有其他一些在这里无关紧要)。
- The signature
void MyMethod(string). - (For those languages that support it) the default value for the single parameter is
"aaa"and therefore when compiling code of the formMyMethod()if no method matchingMyMethod()can be found, you may replace it with a call to `MyMethod("aaa").
- 签名
void MyMethod(string)。 - (对于那些支持它的语言)单个参数的默认值是
"aaa",因此在编译表单的代码时,MyMethod()如果找不到匹配的方法MyMethod(),您可以将其替换为对 `MyMethod("aaa") 的调用。
So, that's what the call in BBBdoes: The compiler sees a call to MyMethod(), doesn't find a method MyMethod()but does find a method MyMethod(string). It also sees that at the place where it is defined there's a default value of "aaa", so at compile time it changes this to a call to MyMethod("aaa").
所以,这就是调用 in 的BBB作用:编译器看到对 的调用MyMethod(),没有找到方法,MyMethod()但确实找到了方法MyMethod(string)。它还看到在定义它的地方有一个默认值“aaa”,所以在编译时它将它更改为对MyMethod("aaa").
From within BBB, AAAis considered the place where AAA's methods are defined, even if overridden in BBB, so that they canbe over-ridden.
从内BBB,AAA被认为AAA是定义 的方法的地方,即使在 中被覆盖BBB,以便它们可以被覆盖。
At run-time, MyMethod(string)is called with the argument "aaa". Because there is a overridden form, that is the form called, but it is not called with "bbb" because that value has nothing to do with the run-time implementation but with the compile-time definition.
在运行时,MyMethod(string)使用参数“aaa”调用。因为有一个被覆盖的表单,即被调用的表单,但它不是用“bbb”调用的,因为该值与运行时实现无关,而是与编译时定义有关。
Adding this.changes which definition is examined, and so changes what argument is used in the call.
添加会this.更改检查的定义,从而更改调用中使用的参数。
Edit: Why this seems more intuitive to me.
编辑:为什么这对我来说似乎更直观。
Personally, and since I'm talking of what is intuitive it can only be personal, I find this more intuitive for the following reason:
就我个人而言,由于我在谈论什么是直观的,它只能是个人的,我觉得这更直观,原因如下:
If I was coding BBBthen whether calling or overriding MyMethod(string), I'd think of that as "doing AAAstuff" - it's BBBs take on "doing AAAstuff", but it's doing AAAstuff all the same. Hence whether calling or overriding, I'm going to be aware of the fact that it was AAAthat defined MyMethod(string).
如果我在编码,BBB那么无论是调用还是覆盖MyMethod(string),我都会认为这是“做事AAA”——它是BBB“做事AAA”,但它做的事AAA都是一样的。因此,无论是调用还是覆盖,我都会意识到它是AAA定义的MyMethod(string)。
If I was calling code that used BBB, I'd think of "using BBBstuff". I might not be very aware of which was originally defined in AAA, and I'd perhaps think of this as merely an implementation detail (if I didn't also use the AAAinterface nearby).
如果我正在调用使用 的代码BBB,我会想到“使用BBB东西”。我可能不太清楚哪个最初是在 中定义的AAA,我可能会认为这仅仅是一个实现细节(如果我没有同时使用AAA附近的接口)。
The compiler's behaviour matches my intuition, which is why when first reading the question it seemed to me that Mono had a bug. Upon consideration, I can't see how either fulfils the specified behaviour better than the other.
编译器的行为符合我的直觉,这就是为什么当我第一次阅读这个问题时,我觉得 Mono 有一个错误。经过考虑,我看不出两者如何比另一个更好地实现指定的行为。
For that matter though, while remaining at a personal level, I'd never use optional parameters with abstract, virtual or overridden methods, and if overriding someone else's that did, I'd match theirs.
尽管如此,在个人层面上,我永远不会将可选参数与抽象、虚拟或覆盖方法一起使用,如果覆盖其他人的方法,我会匹配他们的。
回答by basti
Have you tried:
你有没有尝试过:
public override void MyMethod2()
{
this.MyMethod();
}
So you actually tell your program to use the overriden Method.
所以你实际上告诉你的程序使用覆盖方法。
回答by Marc Gravell
You can disambiguate by calling:
您可以通过调用来消除歧义:
this.MyMethod();
(in MyMethod2())
(在MyMethod2())
Whether it is a bug is tricky; it does look inconsistent, though. Resharper warns you simply not to have changes to the default value in an override, if that helps ;p Of course, resharper alsotells you the this.is redundant, and offers to remove it for you ... which changes the behaviour - so resharper also isn't perfect.
是不是bug就很棘手了;不过,它看起来确实不一致。Resharper 只是警告您不要更改覆盖中的默认值,如果这有帮助;p 当然,resharper也会告诉您这this.是多余的,并提供为您删除它......这会改变行为 - 所以 resharper 也并不完美。
It does look like it couldqualify as a compiler bug, I'll grant you. I'd need to look reallycarefully to be sure... where's Eric when you need him, eh?
看起来它确实有资格作为编译器错误,我会同意。我需要看真仔细,以确保...这里的埃里克·当你需要他,是吧?
Edit:
编辑:
The key point here is the language spec; let's look at §7.5.3:
这里的关键点是语言规范;让我们看看§7.5.3:
For example, the set of candidates for a method invocation does not include methods marked override (§7.4), and methods in a base class are not candidates if any method in a derived class is applicable (§7.6.5.1).
例如,方法调用的候选集不包括标记为覆盖的方法(第 7.4 节),如果派生类中的任何方法适用(第 7.6.5.1 节),则基类中的方法不是候选方法。
(and indeed §7.4 clearly omits overridemethods from consideration)
(实际上第 7.4 节明确忽略了override方法)
There's some conflict here.... it states that the basemethods are not used if there is an applicable method in a derived class - which would lead us to the derivedmethod, but at the same time, it says that methods marked overrideare not considered.
这里有一些冲突......它指出如果派生类中有适用的方法,则不使用基方法 - 这将引导我们使用派生方法,但同时,它表示标记override的方法不是经过考虑的。
But, §7.5.1.1 then states:
但是,第 7.5.1.1 节接着指出:
For virtual methods and indexers defined in classes, the parameter list is picked from the most specific declaration or override of the function member, starting with the static type of the receiver, and searching through its base classes.
对于在类中定义的虚拟方法和索引器,参数列表是从函数成员的最具体的声明或覆盖中选取的,从接收器的静态类型开始,并搜索其基类。
and then §7.5.1.2 explains how the values are evaluated at the time of the invoke:
然后第 7.5.1.2 节解释了在调用时如何评估值:
During the run-time processing of a function member invocation (§7.5.4), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows:
...(snip)...
When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed. Because these are always constant, their evaluation will not impact the evaluation order of the remaining arguments.
在函数成员调用(第 7.5.4 节)的运行时处理期间,参数列表的表达式或变量引用按顺序从左到右求值,如下所示:
……(剪断)……
当从具有相应可选参数的函数成员中省略参数时,将隐式传递函数成员声明的默认参数。因为这些总是常数,所以它们的评估不会影响其余参数的评估顺序。
This explicitly highlights that it is looking at the argument list, which was previously defined in §7.5.1.1 as coming from the most specific declaration or override. It seems reasonable that this is the "method declaration" that is referred to in §7.5.1.2, thus the value passed should be from the most derived up-to the static type.
这明确强调了它正在查看参数列表,该列表先前在 §7.5.1.1 中定义为来自最具体的声明或覆盖。这是第 7.5.1.2 节中提到的“方法声明”似乎是合理的,因此传递的值应该是从最派生的到静态类型。
This would suggest: csc has a bug, and it should be using the derivedversion ("bbb bbb") unless it is restricted (via base., or casting to a base-type) to looking at the base method declarations (§7.6.8).
这表明:csc 有一个错误,它应该使用派生版本(“bbb bbb”),除非它被限制(通过base.,或转换为基类型)以查看基方法声明(第 7.6.8 节) )。
回答by VS1
May be this is due to ambiguity and the compiler is giving priority to the base/super class. The below change to code of your class BBB with adding reference to thiskeyword, gives the output 'bbb bbb':
可能是由于歧义,编译器优先考虑基类/超类。对您的类 BBB 的代码进行以下更改并添加对this关键字的引用,给出输出“bbb bbb”:
class BBB : AAA
{
public override void MyMethod(string s = "bbb")
{
base.MyMethod(s);
}
public override void MyMethod2()
{
this.MyMethod(); //added this keyword here
}
}
One of the things it implies is you should always use the thiskeyword whenever you are calling properties or methods on the current instance of class as a best practice.
它暗示的一件事是,this当您在当前类的实例上调用属性或方法时,您应该始终使用关键字作为最佳实践。
I would be concerned if this ambiguity in base and child method didn't even raise a compiler warning (if not error), but if it does then that was unseen I suppose.
我会担心 base 和 child 方法中的这种歧义是否甚至没有引发编译器警告(如果不是错误),但如果确实如此,我想那是看不见的。
==================================================================
================================================== ================
EDIT:Consider below sample excerpts from these links:
编辑:考虑以下来自这些链接的示例摘录:
Pitfall: Optional parameter values are compile-time There is one thing and one thing only to keep in mind when using optional parameters. If you keep this one thing in mind, chances are you may well understand and avoid any potential pitfalls with their usage: That one thing is this: optional parameters are compile-time, syntactical sugar!
陷阱:可选参数值是编译时使用可选参数时只需要记住一件事和一件事。如果您牢记这一点,那么您很可能会很好地理解并避免使用它们的任何潜在陷阱:这一点是:可选参数是编译时的语法糖!
Pitfall: Beware of Default Parameters in Inheritance and Interface Implementation
陷阱:注意继承和接口实现中的默认参数
Now, the second potential pitfalls has to do with inheritance and interface implementation. I'll illustrate with a puzzle:
现在,第二个潜在的陷阱与继承和接口实现有关。我用一个谜语来说明:
1: public interface ITag
2: {
3: void WriteTag(string tagName = "ITag");
4: }
5:
6: public class BaseTag : ITag
7: {
8: public virtual void WriteTag(string tagName = "BaseTag") { Console.WriteLine(tagName); }
9: }
10:
11: public class SubTag : BaseTag
12: {
13: public override void WriteTag(string tagName = "SubTag") { Console.WriteLine(tagName); }
14: }
15:
16: public static class Program
17: {
18: public static void Main()
19: {
20: SubTag subTag = new SubTag();
21: BaseTag subByBaseTag = subTag;
22: ITag subByInterfaceTag = subTag;
23:
24: // what happens here?
25: subTag.WriteTag();
26: subByBaseTag.WriteTag();
27: subByInterfaceTag.WriteTag();
28: }
29: }
What happens? Well, even though the object in each case is SubTag whose tag is “SubTag”, you will get:
发生什么了?好吧,即使每种情况下的对象都是标签为“SubTag”的 SubTag,你也会得到:
1: SubTag 2: BaseTag 3: ITag
1:子标签 2:基本标签 3:ITag
But remember to make sure you:
但请记住确保您:
Do not insert new default parameters in the middle of an existing set of default parameters, this may cause unpredictable behavior that may not necessarily throw a syntax error – add to end of list or create new method. Be extremely careful how you use default parameters in inheritance hierarchies and interfaces – choose the most appropriate level to add the defaults based on expected usage.
不要在现有的一组默认参数中间插入新的默认参数,这可能会导致不可预测的行为,不一定会引发语法错误——添加到列表末尾或创建新方法。在继承层次结构和接口中使用默认参数的方式要非常小心——选择最合适的级别来根据预期使用添加默认值。
==========================================================================
================================================== ========================
回答by chandmk
This I think is because these default values are fixed at the compile time. If you use reflector you will see the following for MyMethod2 in BBB.
我认为这是因为这些默认值在编译时是固定的。如果您使用反射器,您将在 BBB 中看到 MyMethod2 的以下内容。
public override void MyMethod2()
{
this.MyMethod("aaa");
}
回答by Eric Lippert
The behaviour is definitely very strange; it is not clear to me if it is in fact a bug in the compiler, but it might be.
这种行为肯定很奇怪;我不清楚它是否实际上是编译器中的错误,但可能是。
The campus got a fair amount of snow last night and Seattle is not very good about dealing with snow. My bus is not running this morning so I'm not going to be able to get into the office to compare what C# 4, C# 5 and Roslyn say about this case and if they disagree. I'll try to post an analysis later this week once I'm back in the office and can use proper debugging tools.
昨晚校园下了相当多的雪,西雅图不太擅长处理雪。我的公共汽车今天早上没有运行,所以我无法进入办公室比较 C# 4、C# 5 和 Roslyn 对这个案例的看法以及他们是否不同意。本周晚些时候我回到办公室并可以使用适当的调试工具后,我将尝试发布分析。
回答by Jon Skeet
This looks like a bug to me. I believe it iswell specified,
and that it should behave in the same way as if you call the
method with the explicit thisprefix.
这对我来说似乎是一个错误。我相信它被很好地指定,并且它的行为方式应该与您使用显式this前缀调用方法相同。
I've simplified the example to only use a singlevirtual method, and show both which implementation is called and what the parameter value is:
我已将示例简化为仅使用单个虚拟方法,并显示调用了哪个实现以及参数值是什么:
using System;
class Base
{
public virtual void M(string text = "base-default")
{
Console.WriteLine("Base.M: {0}", text);
}
}
class Derived : Base
{
public override void M(string text = "derived-default")
{
Console.WriteLine("Derived.M: {0}", text);
}
public void RunTests()
{
M(); // Prints Derived.M: base-default
this.M(); // Prints Derived.M: derived-default
base.M(); // Prints Base.M: base-default
}
}
class Test
{
static void Main()
{
Derived d = new Derived();
d.RunTests();
}
}
So all we need to worry about are the three calls within RunTests. The important bits of the spec for the first two calls are section 7.5.1.1, which talks about the parameter list to be used when finding corresponding parameters:
所以我们只需要担心 RunTests 中的三个调用。前两次调用规范的重要部分是第 7.5.1.1 节,其中讨论了查找相应参数时要使用的参数列表:
For virtual methods and indexers defined in classes, the parameter list is picked from the most specific declaration or override of the function member, starting with the static type of the receiver, and searching through its base classes.
对于在类中定义的虚拟方法和索引器,参数列表是从函数成员的最具体的声明或覆盖中选取的,从接收器的静态类型开始,并搜索其基类。
And section 7.5.1.2:
和第 7.5.1.2 节:
When arguments are omitted from a function member with corresponding optional parameters, the default arguments of the function member declaration are implicitly passed.
当从具有相应可选参数的函数成员中省略参数时,将隐式传递函数成员声明的默认参数。
The "corresponding optional parameter" is the bit that ties 7.5.2 to 7.5.1.1.
“相应的可选参数”是将 7.5.2 与 7.5.1.1 联系起来的位。
For both M()and this.M(), that parameter list should be
the one in Derivedas static type of the receiver is Derived,
Indeed, you can tell that the compiler treats
that as the parameter list earlier in the compilation, as if you
make the parameter mandatoryin Derived.M(), bothof the
calls fail - so the M()call requiresthe parameter to have
a default value in Derived, but then ignores it!
对于这两个M()和this.M(),即参数列表应该是一个在Derived静态类型的接收器的是Derived,事实上,你可以告诉编译器将作为参数列表早些时候编译,因为如果你做参数强制性的Derived.M(),两者的调用失败 - 因此M()调用需要参数在 中具有默认值Derived,但随后忽略它!
Indeed, it gets worse: if you provide a default value for the
parameter in Derivedbut make it mandatory in Base, the call
M()ends up using nullas the argument value. If nothing else,
I'd say that proves it's a bug: that nullvalue can't come
from anywhere valid. (It's nulldue to that being the default
value of the stringtype; it always just uses the default value
for the parameter type.)
事实上,情况变得更糟:如果您为 in 中的参数提供了默认值,Derived但在 中强制使用Base,则调用
M()最终会null用作参数值。如果不出意外,我会说这证明这是一个错误:该null值不能来自任何有效的地方。(这是null因为它是string类型的默认值;它总是只使用参数类型的默认值。)
Section 7.6.8 of the spec deals with base.M(), which says that
as wellas the non-virtual behaviour, the expression is considered
as ((Base) this).M(); so it's entirely correct for the base method
to be used to determine the effective parameter list. That means
the final line is correct.
与base.M(),它表示,该规范交易部分7.6.8
,以及作为非虚拟行为,表达被视为((Base) this).M(); 所以使用基本方法来确定有效参数列表是完全正确的。这意味着最后一行是正确的。
Just to make things easier for anyone who wants to see the really odd bug described above, where a value not specified anywhere is used:
只是为了让任何想要查看上述真正奇怪错误的人更容易,其中使用了未在任何地方指定的值:
using System;
class Base
{
public virtual void M(int x)
{
// This isn't called
}
}
class Derived : Base
{
public override void M(int x = 5)
{
Console.WriteLine("Derived.M: {0}", x);
}
public void RunTests()
{
M(); // Prints Derived.M: 0
}
static void Main()
{
new Derived().RunTests();
}
}
回答by yoel halb
Either Way It Needs A Fix
无论哪种方式都需要修复
I would definitely regard it as a bug, either because the results is wrong or if the results are expected then the compiler should not let you declare it as "override", or at least provide a warning.
我肯定会将其视为错误,因为结果是错误的,或者如果结果是预期的,那么编译器不应该让您将其声明为“覆盖”,或者至少提供警告。
I would recommend you to report this to Microsoft.Connect
我建议您将此报告给 Microsoft.Connect
But Is It Right Or Wrong?
但这是对还是错?
However regarding whether this is the expected behavior or not, let us first analyze the two views on it.
然而,关于这是否是预期的行为,让我们先分析一下关于它的两种观点。
consider we have the following code:
考虑我们有以下代码:
void myfunc(int optional = 5){ /* Some code here*/ } //Function implementation
myfunc(); //Call using the default arguments
There are two ways to implement it:
有两种实现方式:
That optional arguments are treated like overloaded functions, resulting in the following:
void myfunc(int optional){ /* Some code here*/ } //Function implementation void myfunc(){ myfunc(5); } //Default arguments implementation myfunc(); //Call using the default argumentsThat the default value is embedded in the caller, thus resulting in the following code:
void myfunc(int optional){ /* Some code here*/ } //Function implementation myfunc(5); //Call and embed default arguments
可选参数被视为重载函数,结果如下:
void myfunc(int optional){ /* Some code here*/ } //Function implementation void myfunc(){ myfunc(5); } //Default arguments implementation myfunc(); //Call using the default arguments默认值嵌入在调用者中,从而产生以下代码:
void myfunc(int optional){ /* Some code here*/ } //Function implementation myfunc(5); //Call and embed default arguments
There are many differences between the two approaches, but we will first take a look on how the .Net framework interprets it.
这两种方法有很多不同之处,但我们将首先看看 .Net 框架如何解释它。
In .Net you can only override a method with a method that contains the same number of arguments, but you cannot override with a method containing morearguments, even if they are all optional (which would result in a call haveing the same signature as the overridden method), say for example you have:
class bassClass{ public virtual void someMethod()} class subClass :bassClass{ public override void someMethod()} //Legal //The following is illegal, although it would be called as someMethod(); //class subClass:bassClass{ public override void someMethod(int optional = 5)}You can overload a method with default arguments with another method with no arguments, (this has disastrous implications as I will discuss in a moments), so the folloing code is legal:
void myfunc(int optional = 5){ /* Some code here*/ } //Function with default void myfunc(){ /* Some code here*/ } //No arguments myfunc(); //Call which one?, the one with no arguments!when using reflection one must always provide a default value.
在 .Net 中,您只能使用包含相同数量参数的方法覆盖方法,但不能使用包含更多参数的方法覆盖,即使它们都是可选的(这将导致调用与调用具有相同的签名)覆盖的方法),例如你有:
class bassClass{ public virtual void someMethod()} class subClass :bassClass{ public override void someMethod()} //Legal //The following is illegal, although it would be called as someMethod(); //class subClass:bassClass{ public override void someMethod(int optional = 5)}您可以使用另一个不带参数的方法重载带有默认参数的方法(这会带来灾难性的影响,我稍后将讨论),因此以下代码是合法的:
void myfunc(int optional = 5){ /* Some code here*/ } //Function with default void myfunc(){ /* Some code here*/ } //No arguments myfunc(); //Call which one?, the one with no arguments!使用反射时,必须始终提供默认值。
All of which are enough to prove that .Net took the second implementation, so the behavior that the OP saw is right, at least according to .Net.
所有这些都足以证明 .Net 采用了第二个实现,因此 OP 看到的行为是正确的,至少根据 .Net 而言。
Problems With the .Net Approach
.Net 方法的问题
However there are real problems with the .Net approach.
然而,.Net 方法确实存在问题。
Consistency
As in the OP's problem when overriding the default value in an inherited method, then results might be unpredictable
When the original implantation of the default value is changed, and since the callers don't have to get recompiled, we might end up with default values that are no longer valid
- Reflection requires you to provide the default value, which the caller doesn't have to know
Breaking code
When we have a function with default arguments and latter we add a function with no arguments, all calls will now route to the new function, thus breaking all existing code, without any notification or warning!
Similar will happen, if we later take away the function with no arguments, then all calls will automatically route to the function with the default arguments, again with no notification or warning! although this might not be the intention of the programmer
Furthermore it does not have to be regular instance method, an extension method will do the same problems, since an extension method with no parameters will take precedence over an instance method with default parameters!
一致性
正如在继承方法中覆盖默认值时 OP 的问题一样,结果可能是不可预测的
当默认值的原始植入发生更改时,并且由于调用者不必重新编译,我们最终可能会得到不再有效的默认值
- 反射要求您提供默认值,调用者不必知道
破解密码
当我们有一个带有默认参数的函数,然后我们添加一个没有参数的函数时,所有调用现在都将路由到新函数,从而破坏所有现有代码,没有任何通知或警告!
类似的情况也会发生,如果我们稍后去掉没有参数的函数,那么所有调用都会自动路由到带有默认参数的函数,同样不会有通知或警告!虽然这可能不是程序员的意图
此外,它不必是常规的实例方法,扩展方法会做同样的问题,因为没有参数的扩展方法将优先于具有默认参数的实例方法!
Summary: STAY AWAY FROM OPTIONAL ARGUMENTS, AND USE INSTEAD OVERLOADS (AS THE .NET FRAMEWORK ITSELF DOES)
总结:远离可选参数,而使用重载(就像 .NET 框架本身所做的那样)
回答by Alexey Khoroshikh
Agree in general with @Marc Gravell.
总体上同意@Marc Gravell。
However, I'd like to mention that the issue is old enough in C++ world (http://www.devx.com/tips/Tip/12737), and the answer looks like "unlike virtual functions, which are resolved at run time, default arguments are resolved statically, that is, at compiled time." So this C# compiler behavior had rather been accepted deliberately due to consistency, despite its unexpectedness, it seems.
但是,我想提一下,这个问题在 C++ 世界(http://www.devx.com/tips/Tip/12737)中已经足够老了,答案看起来像“不像虚拟函数,它在运行时解决时,默认参数是静态解析的,即在编译时。” 因此,尽管看起来出乎意料,但由于一致性,这种 C# 编译器行为被有意接受。

