Резюме для программиста
По-моему, эти названия - new и override - ничего не говорят и выбраны неудачно. Я сколько ни читал описания в МСДН, что этот скрывает, а этот переписывает - так и не понял логики, почему один так называется, а другой так. Они оба скрывают и переписывают, только один в одном случае, а другой в другом. Если кто попытается понять, почему в этом случае вы назвали это сокрытием, а в другом - переписыванием, тот голову сломает и ничего не запомнит. На следующем собесе с каверзными вопросами вы этот вопрос завалите, если вы до этого постоянно эти конструкции не используете с присвоением производных классов базовым и проверке - а что там выводить будет.
По мне, так override скрывает метод базового класса, а new - переписывает. Это понятия, настолько зависящие от контекста, ну типа как разглядывание цифры "6" с противоположных сторон, что если заменить их на "шняжка" и "фигняшка", то в понимании ничего не ухудшится.
Потому что логические действия программы пытаются описать семантическими понятиями ключевых слов, взятых из естественного языка, который нифига не логичен.
По-моему, эти названия - new и override - ничего не говорят и выбраны неудачно.
Все станет понятно как только ты BaseClass определишь так:
class BaseClass { public virtual void Method1() { Console.WriteLine("Base - Method1"); } public /* virtual */ void Method2() { Console.WriteLine("Base - Method2"); } }
А вообще за подобные конствукции надо руки вырывать :)
А вообще за подобные конствукции надо руки вырывать :)
Это всего лишь упрощённые задачки при приёме на работу джуниора. Вот ещё проще. Я тут даже типы не приводил. В реальном коде обычно запутаннее.
Что будет выведено? Без запуска и без подглядывания.
class Program { static void Main(string[] args) { new A().Method1(); new B().Method1(); new C().Method1(); new D().Method1(); A a = new B(); a.Method1(); a.Method2(); A aa = new C(); aa.Method1(); aa.Method2(); A aaa = new D(); aaa.Method1(); aaa.Method2(); B b = new D(); b.Method1(); b.Method2(); } } class A { public void Method1() { Console.WriteLine("A1"); Method2(); } public virtual void Method2() { Console.WriteLine("A2"); } } class B : A { public override void Method2() { Console.WriteLine("B2"); } } class C : A { public new void Method2() { Console.WriteLine("C2"); } } class D : B { public new void Method2() { Console.WriteLine("D2"); } }
Можно немного усложить задачу, если вызывать, например, в методе 2 производного класса метод 1. Можно ещё и метод 1 переопределить, и вызывать их один из другого и 1 из 2 (или наборот). А можно ещё добавить приведение типов перед вызовом. Джуниор должен в уме решать такие задачки. В Новосибирске при устройстве в одну крупную фирму, имеющую филиалы за рубежом, кстати - только так.
Как человек может вообще быть допущен к написанию кода, если не знает таких элементарных вещей? Это как дышать и не понимать, как дышишь - как воздух в лёгкие поступает, как кислород в кровь проходит, окислительно-восстановительные процессы. Стоит только на секунду забыть об этом, как сразу начнёшь задыхаться.
В реальности же всё гораздо сложнее - на все эти наследования ещё и многопоточность накладывается, и лямбды с захватом переменных, и асинхронный вызов делегатов, и ковариация с контравариацией, и unsafe с вызовами сторонних библиотек на других языках, и обработка исключений - всё вместе и одновременно, и быстро починить упавший вдруг сервак, и чтобы ещё вчера. А если баг в инструментарии? А если на ОС пришло обновление с ошибками? И что, что не в тебе дело? А виноват-то ты! Тебе за результат платят, а не за жалобы! А за порогом же толпа стоит - любой тебя готов заменить в любую секунду за меньшую зарплату. Они все ждут, когда ты споткнёшься и допустишь ошибку, не исправишь баг, или откажешься оставаться сверурочно... Я думаю, Шумахер в своём болиде даже под конец гонки чувствует себя куда комфортнее, чем программист за чашечкой кофе в самом начале рабочего дня.
По-моему, эти названия - new и override - ничего не говорят и выбраны неудачно. Я сколько ни читал описания в МСДН, что этот скрывает, а этот переписывает - так и не понял логики, почему один так называется, а другой так. Они оба скрывают и переписывают, только один в одном случае, а другой в другом. Если кто попытается понять, почему в этом случае вы назвали это сокрытием, а в другом - переписыванием, тот голову сломает и ничего не запомнит. На следующем собесе с каверзными вопросами вы этот вопрос завалите, если вы до этого постоянно эти конструкции не используете с присвоением производных классов базовым и проверке - а что там выводить будет.
По мне, так override скрывает метод базового класса, а new - переписывает. Это понятия, настолько зависящие от контекста, ну типа как разглядывание цифры "6" с противоположных сторон, что если заменить их на "шняжка" и "фигняшка", то в понимании ничего не ухудшится.
Потому что логические действия программы пытаются описать семантическими понятиями ключевых слов, взятых из естественного языка, который нифига не логичен.
Даже в МСДН либо почти нигде не объяснено, что означает "скрывает" и "переписывает", либо это разбросано в разных местах в разных примерах кода, так что в голове единая картина не складывается.
Почему, например, override переписывает, а new - скрывает? Взять из моего примера кода
new B().Method1();
Тут внутри метода 1 в базовом классе А вызывается метод 2 производного класса В, а не базового же класса А. Т.е. получается, что override скрыл метод 2 базового класса А? А почему "скрыл" используется для new?
А всё потому, что эта терминология используется для производного класса относительно базового. В производном классе override перезаписывает методы базового класса, а new - скрывает их. Если же мы находимся в базовом классе, то тут лучше использовать другие слова: при вызове базового члена из производного, override заставляет использовать производный член, а new - позволяет использовать базовый. Причём понятия "мы находимся в базовом" или "мы находимся в производном" классах - верны в любом случае. Т.е. что мы находимся в методе какого-то класса, что мы приводим тип.
Например, в случае
new B().Method1();
мы находимся в производном классе, вызываем метод базового, и внутри базового мызываем метод, который есть в обоих классах иерархии наследования, но в производном определён как override. Это заставляет использовать вместо базового метода производный. Т.е. override скрывает метод базового класса.
В случае
new C().Method1();
мы находимся в производном, вызываем метод базового, внутри него - метод, который есть в обоих классах в иерархии, но в производном определён как new. Это позволяет нам использовать в базовом классе базовую версию метода. Здесь new ничего не скрывает, а наоборот, позволяет использовать. Понятие скрывает к этому случаю неприменимо. Сокрытие new базовых членов применимо, когда мы находимся в производном классе и хотим вызвать член, которые есть в обоих классах в иерархии. В объяснениях же в МСДН и прочих переводных, которые обычно переводятся тупо и без осмысления, написана какая-то хрень и абракадабра, которую невозможно понять, т.к. всё описано несоответственно семантике употребляемых терминов, или не уточняется, в каком контексте мы находимся - в контексте базового класса, производного или это случай приведения типов, или случай вызова производного метода из базового метода из производного класса.
Впрочем, мои "позволяет" и "заставляет" тоже нужно убрать.
Итого, как я считаю, правильное понимание ключевых слов new и override:
new:
- при вызове базовых членов из производных вызываются базовые члены;
- при вызове производных из производных вызываются производные члены, если их поведение не переопределено в более производных;
override
- при вызове базовых членов из производных вызываются производные члены;
- при вызове производных из производных вызываются производные члены, если их поведение не переопределено в более производных;
Итого получается, что если в производных классах ничего не переопределено или этих классов вообще нет, то их члены вызываются, как если бы у этих классов не было предков - т.е. нельзя вызвать базовые версии членов. (На самом деле можно, но лучше так не делать.)
Люди годами делают сотни миллионов денег, принимая на работу по таким задачкам, люкам и шарикам в автобусах, а вы им солидами всякими тыкать будете? Солиды и прочие лисковы - для лохов, которые не могут залить баблом свою дурь. )))
Да ладно, просто напишите результат вывода хотя бы по этому, без подгляда и запуска:
A aaa = new D();
aaa.Method1();
aaa.Method2();
B b = new D();
b.Method1();
b.Method2();
Я вот, следуя своей логике, что выше написал, всё правильно решил. А читая МСДНы и прочие описания - нихрена понять не мог, почему у меня не так, как в реальности выводится.
Правда, боюсь, что толку от этого не много, т.к. скоро забуду. А потом полезу опять читать в какой-нибудь МСДН, и снова будет каша в голове с их пояснений. Наверняка все эти условия с new и override внутри Дотнета какой-нибудь пачкой if'ов описываются при разборе иерархии наследования, какую версию какого метода вызвать. А они, вместо того, чтобы просто логическую таблицу с этими if'ами привести, начинают дуть в уши про "тут скрываем, тут перезаписываем", безотносительно того, где и что имеется ввиду.
Вот если устроюсь на работу и доберусь до собесов, ух я тогда отыграюсь на всех! Тоже буду каверзные задачки спрашивать. Ещё и покруче чего придумаю! А начальству буду говорить, что вокруг одни дураки, не знают базовых основ. ))) Нас чморили, теперь мы будем чморить. Дедовщина, короче. )))
Почему? Не вижу. То что B и D перестают из Method1 вызывать Method2 это не нарушение. Изменили поведение - да, но никакой инвариант не сломали.
Потому что принцип фактически сводится к "наследуемый класс должен дополнять, а не замещать поведение базового объекта".
т.е. если ты сделаешь тест для класса А и этот тест выдал зелененький результат, то тест с объектом класса Б (Б унаследован от А) тоже должен давать зеленый результат.
При этом в случае с new есть еще одна засада - программист, который использует Б должен знать делали реализации.
Так что в этом примере плохо абсолютно все.
Ну, тут не в примере дело. new отвратителен сам по себе. Но уж такая у сионистов карма, без корявостей никуда :)
А поведение... Так нет у этих классов никакого поведения. Их методы состояние не изменяют и никакой контракт нигде не задекларирован. Так что и менять нечего. А вывод на консоль это не поведение, это "побочный эффект".