Перед выходными мне попался перевод на русский язык заметки "Hide and seek". В ней рассматривалось правило сокрытия методов при наследовании класса. В примере был приведен код, где обращение к методу базового класса можно было принять за попытку обращения к закрытому методу вне его класса. Но меня заинтересовал другой момент.
В приведенном там примере при наследовании использовался модификатор protected. В чем может быть особенность?
Предположим, есть некий базовый класс Parent. Другой разработчик унаследовал от него свой класс Child. А мы, уже наследуя его, пишем свой класс Grandchild. Это будет выглядеть так:
class Parent { public int F() { return 1; } }
class Child : Parent { }
class Grandchild : Child { public int Y() { return this.F(); } }
Вопрос: чему будет равна переменная result в следующем примере?
Grandchild g = new Grandchild();
bool result = g.F() == g.Y();
Ответ: true. Пока все просто.
Теперь разработчик класса Child объявляет внутри своего класса метод F() вот так:
class Parent { public int F() { return 1; } }
class Child : Parent { new protected int F() { return 2; } }
class Grandchild : Child { public int Y() { return this.F(); } }
Вопрос: а теперь чему будет равна переменная?
bool result = g.F() == g.Y();
Ответ: false.
Тут необходимо вспомнить, что "объявление нового члена скрывает унаследованный только в области видимости нового члена". Т.е. объявление нового метода как protected скроет базовый внутри своего класса и, что важно, его потомков. Но вне класса базовый метод будет по прежнему виден. Поэтому вызов к g.F() по сути будет вызовом Parent.F(). А вот метод g.Y() в своем теле обратиться к Child.F(). В итоге получаем
bool result = 1 == 2;
Отсюда и ответ
false. Если же использовать
private вместо
protected, то получим исходный результат –
true.
Вот еще один повод подумать, прежде чем использовать модификатор protected.
Кроме того, если вы еще не прочитали заметку, на которую я ссылался, или ее оригинал на английском, то рекомендую это сделать. Там объясняется почему используется именно такое правило наследования.
Небольшое дополнение, поскольку в комментариях заговорили об очевидности данного подведения: я не утверждаю, что это не документированное поведение. Более того, выше д��но объяснение почему происходит все именно так. Вопрос использовании модификатора protected. В данном случае, это при наследовании приводит к тому, что визуально одинаковый код (вызов метода F) ведет себя по абсолютно по разному внутри и вне класса. И именно возникающая двойственность поведения мне кажется неправильной.