Почему это работает? Перегрузка метода + переопределение метода + полиморфизм

Обычно причина, почему "instanceof" оператор осужден в случае как этот (где instanceof проверяет на подклассы этого базового класса) состоит в том, потому что то, что необходимо делать, перемещает операции в метод и сверхизбавляет его для соответствующих подклассов. Например, если Вы имеете:

if (o instanceof Class1)
   doThis();
else if (o instanceof Class2)
   doThat();
//...

можно заменить это

o.doEverything();

и затем иметь реализацию "doEverything ()" в вызове Class1 "doThis ()", и в вызове Class2 "doThat ()", и так далее.

12
задан kasey 2 December 2009 в 14:31
поделиться

4 ответа

Установите IETab для Chrome , затем настройте правила, чтобы всегда открывать стартовую страницу с помощью IETab.

попробуйте изменить эту строку:

Descendant d = new Descendant();

на эту и перезапустите:

Base d = new Descendant();

Итак, как бы вы могли тогда вызвать Descendant.Test (String) ?

Моя первая попытка выглядит так. это:

public void Test(Object s)
{
    Console.Out.WriteLine("Descendant.Test(Object=" + s + ")");
    Test((String)s);
}

Это не принесло мне пользы, и вместо этого просто снова и снова вызывала Test (Object) для возможного переполнения стека.

Но следующее работает. Поскольку, когда мы объявляем переменную d как имеющую тип Base , мы в конечном итоге вызываем правильный виртуальный метод, мы также можем прибегнуть к этой уловке:

public void Test(Object s)
{
    Console.Out.WriteLine("Descendant.Test(Object=" + s + ")");
    Base b = this;
    b.Test((String)s);
}

Это будет распечатайте:

Descendant.Test(Object=Test)
Descendant.Test(String=Test)

вы также можете сделать это извне:

Descendant d = new Descendant();
d.Test("Test");
Base b = d;
b.Test("Test");
Console.In.ReadLine();

распечатает то же самое.

Но сначала вы должны знать о проблеме , что совсем другое дело.

Тогда Test (String) ?

Моя первая попытка выглядит так:

public void Test(Object s)
{
    Console.Out.WriteLine("Descendant.Test(Object=" + s + ")");
    Test((String)s);
}

Это не принесло мне пользы, и вместо этого я снова и снова вызывал Test (Object) для возможного переполнения стека

Но работает следующее. Поскольку, когда мы объявляем переменную d как имеющую тип Base , мы в конечном итоге вызываем правильный виртуальный метод, мы также можем прибегнуть к этой уловке:

public void Test(Object s)
{
    Console.Out.WriteLine("Descendant.Test(Object=" + s + ")");
    Base b = this;
    b.Test((String)s);
}

Это будет распечатайте:

Descendant.Test(Object=Test)
Descendant.Test(String=Test)

вы также можете сделать это извне:

Descendant d = new Descendant();
d.Test("Test");
Base b = d;
b.Test("Test");
Console.In.ReadLine();

распечатает то же самое.

Но сначала вы должны знать о проблеме , что совсем другое дело.

Тогда Test (String) ?

Моя первая попытка выглядит так:

public void Test(Object s)
{
    Console.Out.WriteLine("Descendant.Test(Object=" + s + ")");
    Test((String)s);
}

Это не принесло мне пользы, и вместо этого я снова и снова вызывал Test (Object) для возможного переполнения стека

Но работает следующее. Поскольку, когда мы объявляем переменную d как имеющую тип Base , мы в конечном итоге вызываем правильный виртуальный метод, мы также можем прибегнуть к этой уловке:

public void Test(Object s)
{
    Console.Out.WriteLine("Descendant.Test(Object=" + s + ")");
    Base b = this;
    b.Test((String)s);
}

Это будет распечатайте:

Descendant.Test(Object=Test)
Descendant.Test(String=Test)

вы также можете сделать это извне:

Descendant d = new Descendant();
d.Test("Test");
Base b = d;
b.Test("Test");
Console.In.ReadLine();

распечатает то же самое.

Но сначала вы должны знать о проблеме , что совсем другое дело.

следующие работы. Поскольку, когда мы объявляем переменную d как имеющую тип Base , мы в конечном итоге вызываем правильный виртуальный метод, мы также можем прибегнуть к этой уловке:

public void Test(Object s)
{
    Console.Out.WriteLine("Descendant.Test(Object=" + s + ")");
    Base b = this;
    b.Test((String)s);
}

Это будет распечатайте:

Descendant.Test(Object=Test)
Descendant.Test(String=Test)

вы также можете сделать это извне:

Descendant d = new Descendant();
d.Test("Test");
Base b = d;
b.Test("Test");
Console.In.ReadLine();

распечатает то же самое.

Но сначала вы должны знать о проблеме , что совсем другое дело.

следующие работы. Поскольку, когда мы объявляем переменную d относящейся к типу Base , мы в конечном итоге вызываем правильный виртуальный метод, мы также можем прибегнуть к этой уловке:

public void Test(Object s)
{
    Console.Out.WriteLine("Descendant.Test(Object=" + s + ")");
    Base b = this;
    b.Test((String)s);
}

Это будет распечатайте:

Descendant.Test(Object=Test)
Descendant.Test(String=Test)

вы также можете сделать это извне:

Descendant d = new Descendant();
d.Test("Test");
Base b = d;
b.Test("Test");
Console.In.ReadLine();

распечатает то же самое.

Но сначала вы должны знать о проблеме , что совсем другое дело.

10
ответ дан 2 December 2019 в 18:19
поделиться

См. Раздел спецификации языка C #, посвященный поиску элементов и разрешению перегрузки . Метод переопределения производного класса не является кандидатом из-за правил поиска членов, а метод базового класса не является наилучшим соответствием на основе правил разрешения перегрузки.

Раздел 7.3

Во-первых, набор всех доступных (Раздел 3.5) члены с именем N, объявленные в T, и базовые типы (Раздел 7.3.1) T созданы. Объявления, содержащие модификатор переопределения, исключаются из набора. Если элементы с именем N не существуют и не доступны, поиск не дает совпадений, и следующие шаги не оцениваются.

Раздел 7.4.2:

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

5
ответ дан 2 December 2019 в 18:19
поделиться

Как правильно заметили другие, когда предоставляется выбор между двумя применимыми методами-кандидатами в классе, компилятор всегда выбирает тот, который был первоначально объявлен ] "ближе" к классу, который содержит сайт вызова, при изучении иерархии базового класса.

Это кажется нелогичным. Конечно, если существует точное совпадение, объявленное для базового класса, тогда это совпадение лучше, чем неточное совпадение, объявленное для производного класса, да?

Нет. Есть две причины всегда выбирать более производный метод вместо менее производного.

Первая заключается в том, что автор производного класса имеет гораздо больше информации, чем автор базового класса. Автор производного класса знает все как о базовом классе , так и о производном классе, то есть в конце концов, класс, который фактически использует вызывающий. Когда предоставляется выбор между вызовом метода, написанного кем-то, кто знает все, и кем-то, кто знает только кое-что о типе, который использует вызывающий, очевидно, имеет смысл отдавать приоритет вызову метода, написанного разработчиком производного класса.

Во-вторых, , такой выбор приводит к одной из форм Отказа Хрупкого Базового Класса. Мы хотим защитить вас от этого отказа, и поэтому написали правила разрешения перегрузки, чтобы избежать его, когда это возможно.

Подробное объяснение того, как это правило защищает вас от отказа базового класса хрупкости, можно найти в моей статье о тема:

http://blogs.msdn.com/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx

И для статей о других способах использования языков в борьбе с хрупкостью Ситуации базового класса, см .:

http://blogs.msdn.com/b/ericlippert/archive/tags/brittle+base+classes/

4
ответ дан 2 December 2019 в 18:19
поделиться

Потому что так определяется язык. Для виртуальных членов Реализация, которая вызывается во время выполнения, когда метод существует как в базовом, так и в производном классе, основана на конкретном типе объекта, который метод вызывается не для объявленного типа переменной, которая содержит ссылку на объект. Ваш первый MyMethod находится в абстрактном классе. Таким образом, он не может никогда вызываться из объекта типа MyClass - потому что такой объект никогда не может существовать. Все, что вы можете создать, это производный класс MySubClass . Конкретный тип - MySubClass , поэтому вызывается реализация , независимо от того, что вызывающий ее код находится в базовом классе.

Для не виртуальных членов / методов просто верно обратное.

1
ответ дан 2 December 2019 в 18:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: