我正在浏览C#Brainteasers(http://www.yoda.arachsys.com/csharp/teasers.html)并遇到一个问题:此代码的输出应该是什么?
class Base
{
public virtual void Foo(int x)
{
Console.WriteLine ("Base.Foo(int)");
}
}
class Derived : Base
{
public override void Foo(int x)
{
Console.WriteLine ("Derived.Foo(int)");
}
public void Foo(object o)
{
Console.WriteLine ("Derived.Foo(object)");
}
}
class Test
{
static void Main()
{
Derived d = new Derived();
int i = 10;
d.Foo(i); // it prints ("Derived.Foo(object)"
}
}
但是,如果我将代码更改为
class Derived
{
public void Foo(int x)
{
Console.WriteLine("Derived.Foo(int)");
}
public void Foo(object o)
{
Console.WriteLine("Derived.Foo(object)");
}
}
class Program
{
static void Main(string[] args)
{
Derived d = new Derived();
int i = 10;
d.Foo(i); // prints Derived.Foo(int)");
Console.ReadKey();
}
}
我想知道为什么在继承而不是继承时输出会发生变化;为什么方法重载在这两种情况下表现不同?
答案 0 :(得分:19)
正如我在answers页面中指定的那样:
打印Derived.Foo(对象) - 选择过载时,如果有任何兼容 在派生类中声明的方法,将忽略在基类中声明的所有签名 - 即使它们在相同的派生类中被覆盖了!
换句话说,编译器查看在最派生类中新近声明的方法(基于表达式的编译时类型),并查看是否有任何适用的方法。如果是,则使用“最佳”可用。如果不适用,则尝试基类,依此类推。重写的方法不计为在派生类中声明。
有关详细信息,请参阅C#3规范的7.4.3和7.5.5.1节。
现在至于为什么这样指定 - 我不知道。对我来说,在派生类中声明的方法优先于在基类中声明的方法是有意义的,否则你会遇到“脆弱的基类”问题 - 在基类中添加一个方法可能会改变代码的含义。派生类。但是,如果派生类是重写在基类中声明的方法,它就会清楚地意识到它,因此脆弱元素不适用。
答案 1 :(得分:1)
它围绕着范围。在第一个程序中,void Foo(int i)属于Base类。 class Derived只是重新定义了它的行为。
Foo(int i)被忽略,因为它是“借用”并通过类Base的继承重新定义。如果Foo(对象o)不存在,则使用Foo(int i)。
在第二个程序中,调用void Foo(int i),因为它正确属于Derived类(即它没有被借用并通过继承覆盖)并具有最佳的签名拟合。
答案 2 :(得分:0)
这是因为在考虑基类签名之前,首先匹配派生类中的方法签名。因此,当您尝试:
d.Foo(i);
它尝试将方法签名与当前类(而不是基类)进行匹配。它找到了可接受的匹配Foo(object)
,并且没有进一步考虑基类方法签名。
所有这些都与编译器找到匹配的方法签名以及它用于查找这些签名的搜索顺序有关。
-Doug
答案 3 :(得分:0)
真正的问题是为什么C#的设计者认为在这种情况下重载分辨率应该忽略被覆盖的方法?
这样的问题使方法重载了我最不喜欢的语言功能之一。