Я проходил Головоломки C# (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();
}
}
Я хочу к тому, почему вывод становится измененным, когда мы наследовались по сравнению с не наследованием; почему перегрузка метода ведет себя по-другому в этих двух случаях?
Как я указал на странице ответов :
Derived.Foo (объект) печатается - при выборе перегрузки, если существуют любые совместимые методы , объявленные в производном классе, все сигнатуры, объявленные в базовом классе, игнорируются - даже если они переопределяются в том же производном классе!
Другими словами, компилятор просматривает методы, недавно объявленные в наиболее производном классе (на основе типа выражения во время компиляции), и определяет, применимы ли какие-либо из них. Если да, то используется «лучший» из имеющихся. Если ни один из них не подходит, он пробует базовый класс и так далее. Переопределенный метод не считается объявленным в производном классе.
Подробнее см. Разделы 7.4.3 и 7.5.5.1 спецификации C # 3.
Теперь насчет того, почему именно так указано - я не знаю. Для меня имеет смысл, что методы, объявленные в производном классе, имеют приоритет над методами, объявленными в базовом классе, поскольку в противном случае вы столкнетесь с проблемой «хрупкого базового класса» - добавление метода в базовый класс может изменить значение кода с использованием производный класс. Однако, если производный класс переопределяет метод, объявленный в базовом классе, он явно знает об этом, поэтому этот элемент хрупкости не применяется.
Он вращается вокруг области видимости. В первой программе void Foo(int i) принадлежит классу Base. Класс Derived просто переопределяет его поведение.
Foo(int i) игнорируется, потому что она "заимствуется" и переопределяется через наследование от класса Base. Если бы Foo(object o) не существовало, то использовалась бы Foo(int i).
Во второй программе вызывается void Foo(int i), потому что она правильно принадлежит классу Derived (т.е. не заимствуется и не переопределяется через наследование) и имеет наилучшее соответствие сигнатуре.
Это потому, что сигнатуры методов в производном классе сначала сопоставляются, а затем рассматриваются сигнатуры базового класса. Поэтому, когда вы пытаетесь:
d.Foo(i);
Он пытается сопоставить сигнатуру метода с вашим текущим классом (не с базовым классом). Он находит приемлемое совпадение Foo (object)
и больше не рассматривает сигнатуры методов базового класса.
Все дело в том, что компилятор находит соответствующую сигнатуру метода и порядок поиска, который он использует для поиска этих сигнатур.
-Дуг