为什么编译器不能解析方法覆盖?
问题描述:
在下面的C#代码段为什么编译器不能解析方法覆盖?
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal sound");
}
}
public class Dog:Animal
{
public override void MakeSound()
{
Console.WriteLine("Dog sound");
}
}
class Program
{
static void Main(string[] args)
{
Animal an = new Dog();
an.MakeSound();
Console.ReadLine();
}
}
被称为在运行时确定的方法。为什么编译器无法弄清楚,调用哪个方法?
为什么编译器看不到an
引用了Dog
对象,然后从该类中选择方法?
运行时如何确定要调用哪个方法?
答
你告诉编译器该变量是Animal类型的。它只看到声明,而你期望它执行你的代码来找出你的意思。这不是它的工作原理。
答
这听起来像一个考试/家庭作业问题非常多。但让我用另一个问题回答你的问题。 考虑下面的代码:
static void Main(string[] args)
{
var random = new Random();
Animal an = null;
if (random.NextDouble() < 0.5) {
an = new Dog();
} else {
an = new Cat();
}
an.MakeSound();
Console.ReadLine();
}
如何编译器应该在编译时知道哪种方法来调用?它不能,只有在运行时才会知道具体的类型。
答
考虑下面的代码:
class Program
{
static void Main(string[] args)
{
Animal dog = new Dog();
MakeSoundAbstract(dog);
Animal an = new Animal();
MakeSoundAbstract(an);
Console.ReadLine();
}
static void MakeSoundAbstract(Animal animal)
{
animal.MakeSound();
}
}
如果编译器会在运行时编译期间确定的虚拟呼叫不那么MakeSoundAbstract
方法将总是执行class Animal
所以我们正在失去这里的abstraction功率MakeSound
方法。
答
考虑这改变了代码:
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal sound");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}
public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Purrrrrrrrrrrrrr");
}
}
class Program
{
static void Main(string[] args)
{
Animal an = GetAnimal(DateTime.Now);
an.MakeSound();
Console.ReadLine();
}
private Animal GetAnimal(DateTime dateTime)
{
if (dateTime.DayOfWeek == DayOfWeek.Monday)
{
return new Dog();
}
else
{
return new Cat();
}
}
}
现在是不可能知道要创建什么类型的动物在编译时间,因为它取决于星期的代码时实际运行。星期一你会得到一只狗,但在任何时候你会得到一只猫。这是多态性的一个明显的好处 - 类型不是由编译器烘焙的,而是在代码执行时即时导出的。多态性允许您使用这些派生类型,即使您在编写代码时(但您确实知道它们都是所有类型的动物)并不确切知道它们会是什么。
你有没有听说过抽象? –
我说狗的声音,如果我编译它,这对我来说似乎是完全不错的 – BugFinder
他们没有问在运行时的输出是什么。事实上,他们在实际问题中明确表示他们知道运行时正在发生的事情。问题是为什么编译器不知道'an'是编译时的'Dog'。 – Nick