Г. Шилдт - С#4.0 Полное руководство (1160795), страница 68
Текст из файла (страница 68)
с1аяя Тгяапд1е : ТноОБЬаре ( ясг1пд БГУ1е; // Конструктор, используемый по умолчанию. роЫгс Тг1апс1е() ( БГУ1е = "по11"; ) // Конструктор для класса Тг1апс1е. роЪ11с Тг1апс1е (ягг1пд я, с(опЫе н, <(опЫе Ь) Ьаяе (и, Ь, "треугольник" ) ( БГУ1е = я; ) // Сконструировать равнобедренный треугольник. рчЫТс Тг1апд1е(с(опЫе х): Ьаяе(х, "треугольник") ( Ягу1е = "равнобедренный"; ) // Сконструировать копию объекта типа Тг1ап01е. рпЬ11с Тг1апя1е(Тг1апо1е оЫ : Ьаяе(оЫ ( ЯГУ1е = оЬ.БСУ1е( ) // Переопределить метод Агеа() для класса Тг1апс1е. ро(~11с очеггябе НосЫе Агеа() ( геяогп Н1огЬ * Не1слг / 2; ) // Показать тип треугольника. рцЫгс чогН ЯЬонБГУ1е() Сопяо1е.нг1вед1пе("Треугольник " + Ясу1е) ) 362 Часть [.
йзык С() // Класс для прямоугольников, производный от класса ТноОЯЬаре. с1авв Нестапд1е . "ТноОЯЬаре ( // Конструктор для класса Нестапд1е. риЬ1тс Нестапд1е(боиЬ1е н, боиЫе Ы Ьаве(н, Ь, "прямоугольник")( ) // Сконструировать квадрат. риЬ1гс Рестапд1е(боиЬ1е х) Ьаве(х, "прямоугольник" ) ( // Сконструировать копию объекта типа Нестапд1е. риЬ11с Нестапд1е(вестапд1е оЫ : Ьаве(оЫ ( ) // Возвратить логическое значение стие, если // прямоугольник окажется квадратом.
риЬ1тс Ьоо1 гвЯдиате() ( 1Г(нгс(тЬ == Нетдьт) тетитп ттие~ тетитп Га1ве) ) // Переопределить метод Агеа() для класса Нестапд1е. риЫтс очетт1с(е с(оиЫе Агеа () тегитп Итбтл * Нетдьт) ) с1авв ОупЯЬарея ( зтат1с чотб Ма1п() ( ТноОЯЬаре[[ вьарев = пен ТноОЯЬаре[5)г тот(1пт 1=0) г < вьарез.ьепдть| 1++) ( Сопво1е.игттеЬЬпе("Объект — " + яЬарез[т).паше); Сопзо1е.игттеЬЬпе("Площадь равна " + вьарев[1).Агеа()); Сопво1е.ит1теЬЬпе(); ) При выполнении этой программы получается следующий результат. Объект — треугольник Площадь равна 48 Объект — прямоугольник Площадь равна 100 Объект — прямоугольник зьарев[0) зьарев[1) вларев[2) вларев[3) вьарез[4) пен Тгтапд1е("прямоугольный", 8.0, 12.0)) пен Нестапд1е(10)) пен Нестапд1е(10, 4); пен Тттапд1е(7.0)) пен ТноРЯЬаре(10, 20, "общая форма" ); Глава Х1.
Наследование 363 Плошадь равна 40 Объект — треугольник Площадь равна 24.5 Объект — общая форма Метод Агеа() должен быть переопределен Площадь равна О Рассмотрим данный пример программы более подробно. Прежде всего, метод Агеа () объявляется как у1ггпа1 в классе ТноПБПаре и переопределяется в классах Тг1апо1е и Пеогапо1е по Объяснявшимся ранее причинам. В классе ТыоПБПаре метод Агеа () реализован в виде заполнителя, который сообщает о том, что пользователь данного метода должен переопределить его в производном классе.
Каждое переопределение метода Агеа () предоставляет конкретную его реализацию, соответствующую типу объекта, инкапсулируемого в производном классе. Так, если реализовать класс для эллипсов, то метод Агеа ( ) должен вычислять площадь эллипса. У программы из рассматриваемого здесь примера имеется еще одна примечательная особенность. Обратите внимание на то, что в методе Мафп () двумерные формы объявляются в виде массива объектов типа тнопБьаре, но элементам этого массива присваиваются ссылки на объекты классов Тг1апо1е, Песбапо1е и ТноПБПаре. И это вполне допустимо, поскольку по ссылке на базовый класс можно обращаться к объекту прризводного класса. Далее в программе происходит циклическое обращения к элементам данного массива для вывода сведений о каждом объекте. Несмотря на всю свою простоту, данный пример наглядно демонстрирует преимущества наследования и переопределения методов.
Тип объекта, хранящийся в переменной ссылки на базовый класс, определяется во время выполнения и соответственно обусловливает дальнейшие действия. Так, если объект является производным от класса ТыоПБПаре, то для получения его площади вызывается метод Агеа ( ) . Но интерфейс для выполнения этой операции остается тем же самым независимо от типа используемой двумерной формы. Применение абстрактных классов Иногда требуется создать базовый класс, в котором определяется лишь самая общая форма для всех его производных классов, а наполнение ее деталями предоставляется каждому из этих классов.
В таком классе определяется лишь характер методов, которые должны быль конкретно реализованы в производных классах, а не в самом базовом классе. Подобная ситуация возникает, например, в связи с невозможностью получить содержательную реализацию метода в базовом классе. Именно ~акая ситуация была продемонстрирована в варианте класса ТыоПБПаре из предыдущего примера, где метод Агеа () был просто определен как заполнитель. Такой метод не вычисляет и не выводит площадь двумерного объекта любого типа.
Создавая собственные библиотеки классов, вы можете сами убедиться в том, что у метода зачастую отсутствует содержательное определение в контексте его базового класса. Подобная ситуация разрешается двумя способами. Один из них, как показано в предыдущем примере, состоит в том, чтобы просто выдать предупреждающее сообщение. Такой способ может пригодиться в определенных ситуациях, например при отладке, но в практике программирования он обычно не применяется. Ведь в базовом 364 Часть (. Язык С(г классе могут быть объявлены методы, которые должны быть переопределены в производном классе, чтобы этот класс стал содержательным.
Рассмотрим для примера класс Тгъапд1е. Он был бы неполным, если бы в нем не был переопределен метод йгеа () . В подобных случаях требуется какой-то способ, гарантирующий, что в производном классе действительно будут переопределены все необходимые методы. И такой способ в С() имеется. Он состоит в использовании абстрактного метода. Абсглрактныи метод создается с помощью указываемого модификатора типа аЬз Ь хасс. У абстрактного метода отсутствует тело, и поэтому он не реализуется в базовом классе. Это означает, что он должен быть переопределен в производном классе, поскольку его вариант из базового класса просто непригоден для использования. Нетрудно догадаться, что абстрактный метод автоматически становится виртуальным и не требует указания модификатора чъгьпа1. В действительности совместное использование модификаторов уьгспа1 и аЬзсгасг считается ошибкой.
Для определения абстрактного метода служит приведенная ниже общая форма. апясгясс тил имл(список параметров) Как видите, у абстрактного метода отсутствует тело. Модификатор аЬясгасс может применяться только в методах экземпляра, но не в статических методах (я Сап ас). Абстрактными могут быть также индексаторы и свойства. Класс, содержащий один или больше абстрактных методов, должен быть также объявлен как абстрактный, и для этого перед его объявлением с1аз з указывается модификатор аЬзб хасс. А поскольку реализация абстрактного класса не определяется полностью, то у него не может быль объектов.
Следовательно, попытка создать объект абстрактного класса с помощью оператора пеы приведет к ошибке во время компиляции. Когда производный класс наследует абстрактный класс, в нем должны быть реализованы все абстрактные методы базового класса.
В противном случае производный класс должен быть также определен как аЬзсгасс. Таким образом, атрибут аЬзсгасс наследуется до тех пор, пока не будет достигнута полная реализация класса. Используя абстрактный класс, мы можем усовершенствовать рассматривавшийся ранее класс ТыоРЗЬаре.
Для неопределенной двухмерной фигуры понятие площади не имеет никакого смьюла, поэтому в приведенном ниже варианте класса ТыоРЯЬаре метод йгеа () и сам класс Тыопйпаре объявляются как аЬзсгасс. Это, конечно, означает, что во всех классах, производных от класса ТыоРЯЬаре, должен быть переопределен метод йгеа () . Создать абстрактный класс. пятна ЗуяпеюГ аьзгтасс с1аяя тноояьаре пзппге ргг нгбпьг боивге рг1 Пе1зпгг Конструктор, используемый по умолчанию.
рнЬ11с Тыопзпарз() ( Игбвп = Нзгспг = б.б; паве = "пп11"; ) Парамзтризирпванный конструктор. Глава 11. Наследование 365 риЪ1гс ТиоОБЬаре(боиЫе н, боиЬ1е Ь, ясг1по и) ИьбСЬ = и) Не1ОЬС = Ь; паве = и; ) // Сконструировать объект равной ширины и высоты. риЬ1гс Тнобвьаре(боиЬ1е х, яьг1пд и) ( ИгбСЬ = Не1оЬС = х; паве = и; ) Сконструировать копию объекта ТноОБЬаре. риЬ11с ТиоОБЬаре(ТноОБЬаре оЫ ИгбСЬ = оЬ.ИгбСЬ; Не1оьС = оЬ.Негоьг? паве = оь.паве; // Свойства ширины и высоты объекта. риЫгс боиЫе ИгбСЬ ( оеС ( гегигп рг1 нббСЛ? яеС ( рг1 нгбгь = ча1ие < О ? -ча1ие: ча1ие; ) риЫгс боиЫе Не1сЬС ( деС ( геСигп ргг Ле1оьг; ) яеС ( ргг ЬеюВЬС = ча1ие < О ? -ча1ие : ча1ие; ) ) риЫ1с ясгспо паве ( Бес; яес; риЫТс чо1б БЬонбгв() ( Сопяо1е.иг1геь1пе("(Кирина и высота равны " И1бсь + " и " + Не1ОЬС)? ) // Теперь метод Агеа() является абстрактным. риЬ1гс аЬяггасг боиЫе Агеа(); // Класс для треугольников, производный от класса ТноОБЬаре.
с1аяя Тгбапд1е : ТноОБЬаре ( яггспд БСу1е; // Конструктор, используемый по умолчанию. риЬ1гс ТгбапЯ1е() ( Бгу1е = "пи11"; ) Конструктор для класса Тглапо1е. риЫгс Тг1апо1е(яСг1пд я, боиЫе н, боиЫе Ы Ьаяе(н, Л, "треугольник") ( БСу1е = я; ) 366 Часть!. Язык С() // Сконструировать равнобедренный треугольник. рпЬ11с Тг1апд1е(боиЬ1е х) : Ьаве(х, "треугольник") ( Япу1е = "равнобедренный"; // Сконструировать копию объекта типа Тг1апд1е.
рпЬ11с Тгбапд1е(Тгбапд1е оЬ) : Ьазе(оЬ) ( Бву1е = оЬ,ЯСу1е) ) // Переопределить метод Агеа() для класса Тгтапд1е. рпЪ1гс очеггтбе бооЬ1е Агеа() ( геппгп Н1бСЬ * Не1дЬС / 2) ) // Показать тип треугольника. роь11с чотб БЬонЯСу1е() ( Сопво1е.Хг1пеО1пе("Треугольник " + Бпу1е) ) ) // Класс для прямоугольников, производный от класса ТноОБЬаре с1авз Несвапд1е : ТноОБЬаре ( // Конструктор для класса Несгапд1е.