我想知道以下行为:
public class A {
public virtual void Do() { Console.WriteLine("A"); }
}
public class B : A {
public override void Do() { Console.WriteLine("B override"); }
public void Do(int value = 0) { Console.WriteLine("B overload"); }
}
class Program {
public static void Main() {
new B().Do(); // ---> Console: "B overload"
}
}
我希望具有确切签名的重载优先于具有可选参数的另一个重载:我希望在控制台中使用“B override”。相反,程序将“B重载”写入控制台。
即使是resharper失败并陷入陷阱:
... Resharper说带有可选参数的重载被具有确切签名的重载隐藏,但实际上恰恰相反。
现在,如果删除继承,那么它的行为与预期一致,并且resharper警告是合法的:
public class B {
public virtual void Do() { Console.WriteLine("B override"); }
public void Do(int value = 0) { Console.WriteLine("B overload"); }
}
class Program {
public static void Main() {
new B().Do(); // ---> Console: "B override"
}
}
所以问题是:解释这一观察的优先规则是什么?为什么具有精确签名的重载优先于具有可选参数的另一个重载,以防具有精确参数的重载覆盖基本实现?
答案 0 :(得分:4)
为什么具有精确签名的重载优先于具有可选参数的另一个重载,以防具有精确参数的重载覆盖基本实现?
基本上这是遵循规范规则的编译器,即使它们在这种情况下令人惊讶。 (第7.6.5.1节是C#5规范的相关部分。)
编译器会查看"最深的"首先输入 - 即具有目标的编译时类型(在本例中为B
)并尝试查找适用的函数成员忽略任何覆盖基类中声明的方法 >:
候选方法集被简化为仅包含来自大多数派生类型的方法:对于集合中的每个方法CF,其中C是声明方法F的类型,所有方法都在C的基本类型中声明从集合中移除。
和
上述解析规则的直观效果如下:要找到由方法调用调用的特定方法,请从方法调用指示的类型开始,然后继续继承链,直到至少一个适用的,可访问的,找到非重写方法声明。然后对在该类型中声明的适用的,可访问的,非重写方法集执行类型推断和重载解析,并调用由此选择的方法。如果未找到任何方法,请尝试将调用作为扩展方法调用进行处理。
因此,在您的情况下,编译器仅会考虑新引入的方法,发现它是适用的(使用默认参数值)并停止搜索。 (即它没有查看基类中声明的方法)。此时,候选函数成员集只有一个条目,因此在那时没有真正的重载决策。
我有article on overloading显示这种事情,不使用可选参数但使用不同的参数类型 - 请参阅"继承"部分。