Рассмотрим данный пример программы более подробно. Прежде всего, метод Area объявляется как virtual в классе TwoDShape и переопределяется в классах Triangle и Rectangle по объяснявшимся ранее причинам. В классе TwoDShape ме тод Area реализован в виде заполнителя, который сообщает о том, что пользователь данного метода должен переопределить его в производном классе. Каждое переопре деление метода Area предоставляет конкретную его реализацию, соответствующую типу объекта, инкапсулируемого в производном классе. Так, если реализовать класс для эллипсов, то метод Area должен вычислять площадь эллипса.
У программы из рассматриваемого здесь примера имеется еще одна примечатель ная особенность. Обратите внимание на то, что в методе Main двумерные формы объявляются в виде массива объектов типа TwoDShape, но элементам этого массива присваиваются ссылки на объекты классов Triangle, Rectangle и TwoDShape. И это вполне допустимо, поскольку по ссылке на базовый класс можно обращаться к объ екту производного класса. Далее в программе происходит циклическое обращения к элементам данного массива для вывода сведений о каждом объекте. Несмотря на всю свою простоту, данный пример наглядно демонстрирует преимущества наследования и переопределения методов. Тип объекта, хранящийся в переменной ссылки на базо вый класс, определяется во время выполнения и соответственно обусловливает даль нейшие действия. Так, если объект является производным от класса TwoDShape, то для получения его площади вызывается метод Area. Но интерфейс для выполнения этой операции остается тем же самым независимо от типа используемой двумерной формы. Применение абстрактных классов
Иногда требуется создать базовый класс, в котором определяется лишь самая об щая форма для всех его производных классов, а наполнение ее деталями предостав ляется каждому из этих классов. В таком классе определяется лишь характер методов, которые должны быть конкретно реализованы в производных классах, а не в самом ба зовом классе. Подобная ситуация возникает, например, в связи с невозможностью по лучить содержательную реализацию метода в базовом классе. Именно такая ситуация была продемонстрирована в варианте класса TwoDShape из предыдущего примера, где метод Area был просто определен как заполнитель. Такой метод не вычисляет и не выводит площадь двумерного объекта любого типа.
Создавая собственные библиотеки классов, вы можете сами убедиться в том, что у метода зачастую отсутствует содержательное определение в контексте его базового класса. Подобная ситуация разрешается двумя способами. Один из них, как показано в предыдущем примере, состоит в том, чтобы просто выдать предупреждающее со общение. Такой способ может пригодиться в определенных ситуациях, например при отладке, но в практике программирования он обычно не применяется. Ведь в базовом классе могут быть объявлены методы, которые должны быть переопределены в произ водном классе, чтобы этот класс стал содержательным. Рассмотрим для примера класс Triangle. Он был бы неполным, если бы в нем не был переопределен метод Area. В подобных случаях требуется какой-то способ, гарантирующий, что в производном классе действительно будут переопределены все необходимые методы. И такой способ в С# имеется. Он состоит в использовании абстрактного метода.
Абстрактный метод создается с помощью указываемого модификатора типа abstract. У абстрактного метода отсутствует тело, и поэтому он не реализуется в ба зовом классе. Это означает, что он должен быть переопределен в производном классе, поскольку его вариант из базового класса просто непригоден для использования. Не трудно догадаться, что абстрактный метод автоматически становится виртуальным и не требует указания модификатора virtual. В действительности совместное исполь зование модификаторов virtual и abstract считается ошибкой.
Для определения абстрактного метода служит приведенная ниже общая форма. abstract тип имя(список_параметров);
Как видите, у абстрактного метода отсутствует тело. Модификатор abstract мо жет применяться только в методах экземпляра, но не в статических методах (static). Абстрактными могут быть также индексаторы и свойства.
Класс, содержащий один или больше абстрактных методов, должен быть также объявлен как абстрактный, и для этого перед его объявлением class указывается мо дификатор abstract. А поскольку реализация абстрактного класса не определяется полностью, то у него не может быть объектов. Следовательно, попытка создать объект аб страктного класса с помощью оператора new приведет к ошибке во время компиляции.
Когда производный класс наследует абстрактный класс, в нем должны быть реа лизованы все абстрактные методы базового класса. В противном случае производный класс должен быть также определен как abstract. Таким образом, атрибут abstract наследуется до тех пор, пока не будет достигнута полная реализация класса.