На этом шаге мы приведем общие сведения об абстрактных классах и особенностях их применения.
Есть такое понятие, как абстрактный метод. Абстрактным называется метод, который в классе только объявлен, но не описан. Под объявлением метода подразумевается ситуация, когда в теле класса указан тип результата метода, его название и приведен список аргументов (после закрывающей круглой скобки ставится точка с запятой), а тела метода нет. То есть блока из фигурных скобок с командами, выполняемыми при вызове метода, нет вообще. Но чтобы метод был абстрактным, недостаточно описать его без тела с командами. В описании метода необходимо явно указать, что метод абстрактный. Для этого используется ключевое слово abstract. Шаблон описания абстрактного метода представлен ниже:
доступ abstract тип имя(аргументы);
Сначала указывается спецификатор уровня доступа, затем ключевое слово abstract, после него - идентификатор типа результата метода, название и список аргументов. При этом аргументы описываются, как и в обычном (не абстрактном) методе, с указанием типа аргумента и его формального названия. В конце ставится точка с запятой.
Если в классе есть хотя бы один абстрактный метод, то такой класс также считается абстрактным. Абстрактный класс, как и абстрактный метод, описывается с ключевым словом abstract.
Поскольку у абстрактного метода нет тела и не известно, какие команды должны выполняться при вызове метода, то такой метод вызвать нельзя. Поэтому, как вы могли догадаться, на основе абстрактного класса нельзя создать объект. В этом случае все логично: если бы такой объект можно было создать, то у него был бы метод, который нельзя вызвать. А это, как минимум, странно и непоследовательно. Но тогда возникает вопрос: а зачем вообще нужны абстрактные классы? Ответ состоит в том, что абстрактный класс может (и должен) использоваться как базовый при наследовании. Это его главное и наиболее важное назначение - быть базовым классом. Общая схема такова: создается абстрактный класс, а затем путем наследования на основе абстрактного класса создаются обычные (не абстрактные) производные классы. При этом в производных классах абстрактные методы переопределяются: описывается полная версия метода, и, как при переопределении обычных методов, в описании указывается ключевое слово override. В производном классе должны быть переопределены (описаны) все абстрактные методы из абстрактного базового класса.
Преимущество использования абстрактного класса как базового состоит в том, что, описывая базовый класс, мы фактически создаем некий шаблон для производных классов. Все производные классы, созданные на основе одного и того же абстрактного класса, будут иметь определенный набор методов. В каждом из классов эти методы реализуются по-своему, но они есть, и мы в этом можем быть уверены.
Конечно, никто не запрещает нам описать обычный (не абстрактный) класс и затем на его основе создавать производные классы, переопределяя в них методы их базового класса. Но это не самый лучший подход, поскольку если забыть переопределить в производном классе метод (который надо переопределить), то в производном классе будет использована унаследованная версия метода из базового класса и формальной ошибки в этом не будет. А такие ситуации сложно отслеживать. Делая же методы абстрактными в базовом абстрактном классе, мы с необходимостью должны будем описать их в производном классе. Если этого не сделать, программа просто не скомпилируется.
Также следует заметить, что хотя создать объект абстрактного класса нельзя, но можно объявить объектную переменную для абстрактного класса. Эта переменная не может ссылаться на объект абстрактного класса (поскольку такой объект создать нельзя), зато она может ссылаться на объект производного класса. А поскольку абстрактные методы по умолчанию являются виртуальными и переопределяются в производном классе, то через объектную переменную базового абстрактного класса будут вызываться именно те версии методов, что определены в производном классе. Далее перейдем к рассмотрению примеров.
В производном классе свойство или индексатор описываются с ключевым словом override, как при переопределении метода. При этом должны быть описаны все аксессоры, объявленные в абстрактном классе для данного свойства или индексатора.
На следующем шаге мы рассмотрим использование абстрактных классов.