Как объяснить такое поведение с помощью перегруженных и переопределенных методов?

Может ли кто-нибудь быть таким любезным и объяснить мне, почему этот код показывает Derived.DoWork(double). Я могу придумать несколько объяснений этому поведению, однако я хочу, чтобы кто-нибудь разъяснил это мне.

using System;

public class Base
{
    public virtual void DoWork(int param) {
        Console.WriteLine("Base.DoWork");
    }
}

public class Derived : Base
{
    public override void DoWork(int param) {
        Console.WriteLine("Derived.DoWork(int)");
    }

    public void DoWork(double param) {
        Console.WriteLine("Derived.DoWork(double)");
    }

    public static void Main() {
        int val = 5;
        Derived d = new Derived();
        d.DoWork(val);
    }
}

person mic4ael    schedule 12.05.2014    source источник
comment
См. 7.3 Поиск элементов. всех доступных (раздел 3.5) элементов с именем N, объявленных в T, и базовых типов (раздел 7.3.1) T. Объявления, включающие модификатор переопределения, исключаются из набора. Если элементы с именем N не существуют и не доступны, поиск не дает совпадений, и следующие шаги не оцениваются.   -  person Habib    schedule 12.05.2014


Ответы (3)


Эрик Липперт всегда говорил "Чем ближе, тем лучше".

Метод, впервые объявленный в производном классе, ближе, чем метод, впервые объявленный в базовом классе.

Таким образом, из приведенной выше ссылки производный класс ближе, поэтому он выбран.

Это поведение тщательно реализовано, чтобы избежать Проблема хрупкого базового класса

Для полноты я поделюсь пулями:

  • Метод, впервые объявленный в производном классе, ближе, чем метод, впервые объявленный в базовом классе.

  • Метод вложенного класса ближе, чем метод содержащего класса.

  • Любой метод принимающего типа ближе, чем любой метод расширения.

  • Метод расширения, найденный в классе во вложенном пространстве имен, ближе, чем метод расширения, найденный в классе во внешнем пространстве имен.

  • Метод расширения, найденный в классе в текущем пространстве имен, ближе, чем метод расширения, найденный в классе в пространстве имен, указанном в директиве using.

  • Метод расширения, найденный в классе в пространстве имен, упомянутом в директиве using, где директива находится во вложенном пространстве имен, ближе, чем метод расширения, найденный в классе в пространстве имен, упомянутом в директиве using, где директива находится во внешнем пространстве имен.

person Sriram Sakthivel    schedule 12.05.2014
comment
Именно об этом ответе я и думал! - person mic4ael; 12.05.2014
comment
Он передает целое число в d.DoWork, поэтому он должен печатать Derived.DoWork(int), а не Derived.DoWork(double). - person Trisped; 12.05.2014
comment
См. также stackoverflow .com/questions/1451099/ - person John Saunders; 12.05.2014
comment
Я все еще ожидаю, что метод с точным соответствием сигнатуры метода будет ближе, чем перегрузка производного класса с немного другой сигнатурой. Живи и учись, наверное. Теперь я должен посмотреть, что делает VB... - person Bradley Uffner; 12.05.2014
comment
@Trisped Внимательно прочитайте это. Метод, впервые объявленный в производном классе, ближе, чем метод, впервые объявленный в базовом классе, фактический метод сначала объявлен только в базовом классе, поэтому я думаю, что он не ближе. - person Sriram Sakthivel; 12.05.2014
comment
@Trisped, хотя Derived переопределяет DoWork(int), компилятор по-прежнему считает, что этот метод принадлежит Base. DoWork(double) совместим, так как int неявно приводится к double, а также определяется в Derived, поэтому он ближе. В качестве доказательства, если вы измените Derived на new public void DoWork(int), вы увидите, что ключевое слово new сообщает компилятору, что DoWork(int) теперь принадлежит Derived, и вместо этого будет соответствовать ему. - person Ivaylo Slavov; 12.05.2014
comment
@BradleyUffner Это работает таким образом, чтобы избежать проблемы хрупкого базового класса. Проверьте обновленную ссылку. - person Sriram Sakthivel; 12.05.2014
comment
Я протестировал его, и, если кому-то интересно, VB.NET называет версию Integer. Интересный выбор разных языковых команд. - person Bradley Uffner; 12.05.2014

Это поведение определено в спецификации языка C#, в частности, в разделе 7.5.3 «Разрешение перегрузки». Вот ссылка на более раннюю версию, в противном случае обратитесь к CSharp Language Specification.docx, который должен быть у вас локально, например, C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#\Specifications\1033\CSharp Language Specification.docx .

В этом случае методы с пометкой override исключаются, поэтому перегрузка double является единственным допустимым вариантом (выделено мной):

Каждый из этих контекстов определяет набор членов функции-кандидата и список аргументов по-своему, как подробно описано в разделах, перечисленных выше. Например, набор кандидатов для вызова метода не включает методы, помеченные как переопределение (§7.4), а методы в базовом классе не являются кандидатами, если применим любой метод в производном классе (§7.6). .5.1).

person Ahmad Mageed    schedule 12.05.2014
comment
Мне любопытно, будут ли какие-либо ситуации, когда именно это поведение будет полезно и будет использоваться намеренно? По крайней мере, команда разработчиков C# определенно что-то задумала. Для людей, знакомых с другими языками ООП, такими как, например, Java (включая меня), такое поведение непрозрачно и, возможно, несправедливо рассматривается как неразумное. - person Ivaylo Slavov; 12.05.2014
comment
Собственно, я нашел причину здесь, в блоге Эрика Липперта. Он также присутствует в одном из ответов на повторяющийся вопрос StackOverflow. . - person Ivaylo Slavov; 12.05.2014

Такое поведение, по-видимому, задумано:

«При выборе перегрузки, если в производном классе объявлены какие-либо совместимые методы, все сигнатуры, объявленные в базовом классе, игнорируются, даже если они переопределены в том же производном классе!» http://social.msdn.microsoft.com/Forums/vstudio/en-US/a70b25d4-f310-4d06-9dc2-a453f822f4f3/функция-не-вызывается-при-перегрузке-функции-из-базового-класса-в-производном-классе-с?forum=csharpgeneral

person edtheprogrammerguy    schedule 12.05.2014