Найти в Дзене
"Мы"-Прогер

Изучаем C# - abstract protected-методы

Из предыдущей статьи мы знаем, что модификатор доступа protected используется, когда поле/свойство/метод должны быть видны в классах-наследниках, но не видны снаружи класса. Сейчас мы разберём пример применения этого слова. Из статьи мы знаем, что один класс может наследоваться от другого. Например, черепаха наследуется от животного. При этом класс-наследник (черепаха) автоматически включает в себя всю логику (то есть все поля, свойства и методы) базового класса (животное). Говоря простым языком, черепаха является животным. Сделаем животным, чтобы каждый вид животных произносил что-нибудь при передвижении. Черепаха должна шипеть "Пш-ш-ш!", собака - лаять "Гав!", гепард - рычать "Р-р-р!". Эта логика должна вызываться непосредственно при передвижении, то есть, из метода Move() базового животного. Но базовое животное ничего не знает о том, какие у него бывают классы-наследники. Поэтому мы вынесем эту логику в отдельный метод Say(), который у каждого наследника будет свой, и будем вызывать
Оглавление

Предыдущие статьи:

Из предыдущей статьи

мы знаем, что модификатор доступа protected используется, когда поле/свойство/метод должны быть видны в классах-наследниках, но не видны снаружи класса. Сейчас мы разберём пример применения этого слова.

Из статьи

мы знаем, что один класс может наследоваться от другого. Например, черепаха наследуется от животного. При этом класс-наследник (черепаха) автоматически включает в себя всю логику (то есть все поля, свойства и методы) базового класса (животное). Говоря простым языком, черепаха является животным.

Пример protected-метода

Сделаем животным, чтобы каждый вид животных произносил что-нибудь при передвижении. Черепаха должна шипеть "Пш-ш-ш!", собака - лаять "Гав!", гепард - рычать "Р-р-р!". Эта логика должна вызываться непосредственно при передвижении, то есть, из метода Move() базового животного. Но базовое животное ничего не знает о том, какие у него бывают классы-наследники. Поэтому мы вынесем эту логику в отдельный метод Say(), который у каждого наследника будет свой, и будем вызывать этот метод из Move() базового животного:

Базовое животное
Базовое животное
Наследник
Наследник

Поскольку каждый класс-наследник будет переопределять метод Say() на свой лад, то этот метод должен быть protected (чтобы быть видимым на уровне наследников). Также он должен быть virtual, чтобы его можно было переопределить.

Абстрактные методы и классы

А что должно делать базовое животное в методе Say()? Поскольку мы не знаем его вид, то не можем сказать, как оно разговаривает. Вообще говоря, базовых животных нет в природе. То есть, то, что мы можем создать объект класса "базовое животное" - это ненормально. Поэтому мы имеем полное право не реализовывать метод Say() базового животного. Метод, помеченный как abstract, не содержит реализации. Абстрактный метод может быть реализован позже, в классах-наследниках:

-3

Более того, мы даже можем вызывать абстрактный метод у базового животного:

-4

Мы не можем только одного - создать объект класса "Базовое животное". Почему? Подумаем, что происходило бы, если бы мы создали такой объект и вызвали у него метод Say(). Скорее всего, надо было бы выдавать ошибку. Но ошибки во время работы программы - это хуже, чем во время компиляции, потому что их можно долго не замечать. Поэтому запрет устанавливается на уровне синтаксиса: абстрактный метод может находиться только в абстрактном классе, а объекты абстрактного класса нельзя создать. Поэтому сейчас у нас на абстрактном методе горит ошибка:

Ошибка "Abstract method in non-abstract class"
Ошибка "Abstract method in non-abstract class"

"Абстрактный метод в неабстрактном классе".

Чтобы её исправить, нужно сделать абстрактным целый класс:

-6

Но теперь нельзя создать объекты класса BaseAnimal:

-7

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

Итак, когда мы вызываем абстрактный метод, то мы всегда имеем какую-то конкретную его реализацию: метод вызывается у объекта-наследника класса, а раз мы можем сконструировать такой объект, значит, этот класс-наследник не абстрактный; а раз класс не абстрактный, то все методы в нём имеют реализацию.

Также интересный момент: если мы забудем реализовать метод Say() у одного из классов-наследников (например, у собаки), то программа не скомпилируется:

Abstract inherited member 'void ...Say()' is not implemented
Abstract inherited member 'void ...Say()' is not implemented

"Абстрактный унаследованный член 'void ...Say()' не реализован".

Это очень удобно - мы сразу узнаём об ошибках. Есть и множество других ошибок, которые обнаруживаются сразу при компиляции. Этим полезны строго типизированные языки.

Реализуйте метод Say() в оставшихся классах-наследниках.

Далее

Абстракции в ООП. Интерфейсы - https://dzen.ru/a/aaGEnEDqKBVwIK4z?share_to=link

Оглавление - https://dzen.ru/a/aXisxwt_Mnz2qTjs?share_to=link