C#重写方法上的可选参数
在.NET Framework中,重写该方法时,似乎存在可选参数问题。以下代码的输出为: “bbb” “aaa” . 但我期望的结果是: “bbb” “bbb” .有解决办法吗。我知道可以用方法重载来解决这个问题,但我想知道原因是什么。代码在Mono中也可以正常工作C#重写方法上的可选参数,c#,.net,overriding,optional-parameters,C#,.net,Overriding,Optional Parameters,在.NET Framework中,重写该方法时,似乎存在可选参数问题。以下代码的输出为: “bbb” “aaa” . 但我期望的结果是: “bbb” “bbb” .有解决办法吗。我知道可以用方法重载来解决这个问题,但我想知道原因是什么。代码在Mono中也可以正常工作 class Program { class AAA { public virtual void MyMethod(string s = "aaa") { Con
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();
}
}
您是否尝试过:
public override void MyMethod2()
{
this.MyMethod();
}
因此,您实际上告诉您的程序使用覆盖的方法。您可以通过调用:
this.MyMethod();
(在MyMethod2()
中)
它是否是一个bug是很棘手的;不过,这看起来确实不一致。Resharper警告您不要更改覆盖中的默认值(如果有帮助);p当然,resharper也会告诉您这个。
是多余的,并提供给您删除它。。。这改变了行为-因此resharper也不是完美的
我同意你的说法,它看起来确实是一个编译器错误。我需要仔细地看一看,以确定。。。你需要埃里克的时候他在哪里
编辑: 这里的关键点是语言规范;让我们看看§7.5.3: 例如,方法调用的候选集不包括标记为override的方法(§7.4),如果派生类中的任何方法适用(§7.6.5.1),则基类中的方法不是候选方法 (事实上,§7.4明确忽略了
覆盖
方法)
这里有些冲突。。。。它指出,如果派生类中存在适用的方法,则不使用基方法,这将导致我们使用派生方法,但同时,它表示不考虑标记为override
的方法
但是,§7.5.1.1规定:
对于类中定义的虚拟方法和索引器,参数列表从函数成员的最特定声明或重写中选取,从接收方的静态类型开始,搜索其基类
然后§7.5.1.2解释了调用时如何评估值:
在函数成员调用的运行时处理过程中(§7.5.4),参数列表的表达式或变量引用按从左到右的顺序计算,如下所示:
…(剪断)
当从具有相应可选参数的函数成员中省略参数时,将隐式传递函数成员声明的默认参数。因为这些参数总是常量,所以它们的计算不会影响其余参数的计算顺序
这明确强调了它正在查看参数列表,该列表先前在§7.5.1.1中定义为来自最具体的声明或覆盖。这似乎是合理的,即§7.5.1.2中提到的“方法声明”,因此传递的值应该从最派生到静态类型
这表明:csc有一个bug,它应该使用派生的版本(“bbb bbb”),除非它被限制(通过
base.
,或强制转换为基类型)查看基方法声明(§7.6.8)。这可能是由于不明确,编译器优先考虑基类/超类。下面对类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
}
}
它暗示的一点是,无论何时调用类的当前实例上的属性或方法,都应该始终使用this
关键字作为最佳实践
如果base和child方法中的这种歧义甚至没有引起编译器警告(如果不是错误的话),我会担心,但如果它引起了警告,那么我想这是看不见的
==================================================================
<强>编辑:< /强>考虑以下链接中的示例摘录:
陷阱:可选参数值是编译时 在使用可选参数时,有一件事,而且只有一件事需要记住。如果您牢记这一点,您可能会很好地理解并避免其使用中的任何潜在陷阱: 有一件事是这样的:可选参数是编译时,语法糖强> 陷阱:注意继承和接口实现中的默认参数 现在,第二个潜在陷阱与继承和接口实现有关。我将用一个谜题来说明: 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: }
会发生什么?即使每种情况下的对象都是标记为“SubTag”的SubTag,您也会得到:
1:子标签
2:基本标签
3:ITag
但请记住确保:
不要在默认参数席中插入新的默认参数,这可能导致不可预测的行为,可能不必引发语法错误——添加到列表的末尾或创建新方法。 在继承层次结构和接口中使用默认参数时要格外小心——根据预期使用情况选择最合适的级别来添加默认值
============================================================================我想这是因为这些默认值在编译时是固定的。如果您使用reflector,您将在BBB中看到MyMethod2的以下内容
public override void MyMethod2()
{
this.MyMethod("aaa");
}
这里值得注意的一点是,每次都会调用重写的版本。将覆盖更改为:
public override void MyMethod(string s = "bbb")
{
Console.Write("derived: ");
base.MyMethod(s);
}
输出为:
derived: bbb
derived: aaa
类中的方法可以执行以下一个或两个操作:
void myfunc(int optional){ /* Some code here*/ } //Function implementation
void myfunc(){ myfunc(5); } //Default arguments implementation
myfunc(); //Call using the default arguments
BBB
中,调用MyMethod()
调用AAA
中定义的方法
是
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();
}
}
void myfunc(int optional = 5){ /* Some code here*/ } //Function implementation
myfunc(); //Call using the 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
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!