Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 67
Текст из файла (страница 67)
// Применить виртуальные методы и полиморфизм. оагпд ЗуасеюК с1ааа Тнопэпаре ( к(онЬ1е ргк нгк(ГЬк ПонЫе ргь Ьегдпгк // Конструктор по умолчанию. рнь11с Тноввпаре () ( И1к)ГЬ = Не10ЬГ = О. 0; Глава ((. Наследование 347 паве = "пи11"; // Параметризированный конструктор. риЬ11с Тиобвпаре(боиЫе и, боиЬ1е Ь, яггспи и) ( ИТОСЬ = и? НеюаЬС = Ь? паве = и; ) // Сконструировать объект равной ширины и высоты.
риЫас ТноОЯЬаре(боиЫе х, ягггпп и) ( И1бСЬ = Несопг = х) паве = и; // Сконструировать копию объекта ТиоОЯЬаре. риЬ1гс Тмо05варе(Тио05Ьаре оЫ ( И1ССЬ = ОЬ.ИгеСЬ) Немапг = оЬ.Не1ОЬС? паве = оЬ.паве) ) // Свойства ширины и высоты объекта. риЬ11с боиЬ1е И) ИСЬ ( дес ( гесигп рга мгбс)м ) яег [ рг1 и16СЬ = ча1ие < 0 ? -ча1ие: ча1ие; ) риЬ11с боиЬ1е Не1ОЬС ( наес ( гесигп рга Ье10ЬС? ) яег ( рг1 ЬемаЬС на1ие < 0 ? -на1ие: ча1ие; ) ) риЬ11с ягг1пд паве ( дег? яег; риЫгс чогб 5Ьоибав() ( Сопяо1е.Иг1Сесгпе("Ширина и высота равны " + ИГИСЬ + " И " + НЕ1ОЬС)) риЫ1с наггиа1 боиЫе Агеа() ( Сопяо1е.иг1ге( апе( "Метод Агеа() должен быть переопределен")? гегигп 0.0; ) ) // Класс для треугольников, производный от // класса ТиоРЯЬаре. с1аяя Тг1апс1е: тноОЯЬаре ( ягг1по ЯСу1е; // Конструктор, используемый по умолчанию.
348 Часть(. Язык Са рцЫЕс Тгаапд1е() ( Бку1е = "пц11") ) // Конструктор для класса Тг1апд1е. рцЬ11с Тгаапд1е(якгапд я, боцЬ1е н, боцЬ1е Ь) Ьаяе(н, Ь, "треугольник") ( Бгу1е = ят ) // Сконструировать равнобедренный треугольник. рцЬ11с Тгаапд1е(боцЬ1е х): Ьаве(х, "треугольник") ( Бгу1е = "равнобедренный"; ) // Сконструировать копию объекта типа Тг1апд1е. рцЫЕс тгаапд1е(тггапд1е оЬ) : Ьаяе(оЫ ( Бсу1е = оЬ.5ьу1е; ) // Переопределить метод Агеа() для класса Тгаапд1е. рцЫЕс очеггабе ЙоцЬ1е Агеа() ( гесцгп Икбсп * Не1дЬГ / 2т // Показать тип треугольника. рцЫ1с чоаб Бпонасу1е() ( Сопяо1е.игагеьапе("Треугольник " + 5су1е)т // Класс для прямоугольников, производный от класса Тнаб5паре. с1аяя Вессапд1е: Тнобапаре ( // Конструктор для класса Нессапд1е.
рцЫ1с Кессапд1е(боцЬ1е н, боцЫе Ы Ьаяе(н, Ь, "гессапд1е")( ) // Сконструировать квадрат. рцЫЕс Кескапд1е (боцЫе х) Ьаяе(х, "прямоугольник") ( ) // Сконструировать копию объекта типа Кессапд1е. рцыас несгапд1е(несгапд1е оь): ьаяе(оы ( ) // Возвратить логическое значение Ггце, если // прямоугольник окажется квадратом. рцЬ11с Ьоо1 1вадцаге() 1Е(ЫЕт)ГЬ == Неадпс) гесцгп Ггце) гегцгп Еа1яе; // Переопределить метод Агеа() дяя класса Кессапд1е. рцЬ11с очеггабе боцЬ1е Агеа() ( гекцгп ЫЕНГЬ * Не1дпст Глава !!. Наследование 349 о1авв Оупзьарев ( ввавтс тоги Маго() ( Тнобзьаре[) вьарев = пен Тнопзларе[5] аьарев[0) = пен Тг[апд1е("прямоугольный", 8.0, 12.0); внарея(1) = пен Кесвапд1е(10)) виарев(2) = пен Кесвапд1е(10, 4); вьареа(3] = пен Тгтап01е(7.0)) внарев[4) = пеи Тнопзьаре(10, 20, "общая форма"); Гог(1пв 1=0; 1 < вварев.ьепфвьл 1+4) ( Сопво1е.игтсеЬЬпе("Объект — " + вьарев[1].паве); Сопво1е.нгтвеьлпе( "Площадь равна " + вьарев[1).лгеа())7 Сопяо1е.нгтвеЬЬпе()) ) ) ) При выполнении этой программы получается следующий результат: Объект — треугольник Площадь равна 48 Объект — прямоугольник Плошадь равна 100 Объект — прямоугольник Площадь равна 40 Объект — треугольник Плошадь равна 24.5 Объект — общая форма Метод Агеа() должен быть переопределен Площадь равна 0 Рассмотрим данный пример программы более подробно.
Прежде всего, метод Агеа () объявляется как утгбпа1 в классе тыоОППаре и переопределяется в классах тг1апо1е и Несбапо1е по объяснявшимся ранее причинам. В классе тноОППаре метод Агеа () реализован в виде заполнителя, который сообщает о том, что пользователь данного метода должен переопределить его в производном классе. Каждое переопределение метода Агеа () предоставляет конкретную его реализацию, соответствующую типу объекта, инкапсулируемого в производном классе. Так, если реализовать класс для эллипсов, то метод Агеа () дОЛжеи Выннслять Плошадь ЭллнПСа.
У программы из рассматриваемого здесь примера имеется еще одна примечательная особенность. Обратите внимание на то, что в методе Ма1п () двумерные формы объявляются в виде массива объектов типа тнорзпаре, но элементам этого массива присваиваются ссылки на объекты классов тг1апо1е, Несбапп1е и тыоОППаре. И это вполне допустимо, поскольку по ссылке на базовый класс можно обращаться к объекту произво- 350 Часть ). Язык С№ диого класса. Далее в программе происходит циклическое обращения к элементам данного массива для вывода сведений о каждом объекте.
Несмотря на всю свою простоту, данный пример наглядно демонстрирует преимушества наследования и переопределения методов. Тип объекта, хранящийся в переменной ссылки на базовый класс, определяется во время выполнения и соответственно обусловливает дальнейшие действия. Так, если объект является производным от класса тыо()зпаре, то для получения его площади вызывается метод Агеа О. Но интерфейс для выполнения этой операции остается тем же самым независимо от типа используемой двумерной формы. Применение абстрактных классов Иногда требуется создать базовый класс, в котором определяется лишь самая общая форма для всех его производных классов, а наполнение ее деталями предоставляется каждому из этих классов. В таком классе определяется лишь характер методов, которые должны быть конкретно реализованы в производных классах, а не в самом базовом классе.
Подобная ситуация возникает, например, в связи с невозможностью получить содержательную реализацию метода в базовом классе. Именно такая ситуация была продемонстрирована в варианте класса ГиоРЯпаре из предыдущего примера, где метод Агеа () был просто определен как заполнитель. Такой метод не вычисляет и не выводит площадь двумерного объекта любого типа. Создавая собственные библиотеки классов, вы сами убедитесь в том, что у метода зачастую отсутствует содержательное определение в контексте его базового класса. Подобная ситуация разрешается двумя способами. Один из них, как показано в предыдущем примере, состоит в том, чтобы просто выдать предупреждающее сообщение.
Такой способ может пригодиться в определенных ситуациях, например при отладке, но в практике программирования он обычно не применяется. Ведь в базовом классе могут быть объявлены методы, которые должны быть переопределены в производном классе, чтобы этот класс стал содержательным. Рассмотрим для примера класс тг1апп1е. Он был бы неполным, если бы в нем не был переопределен метод Агеа () .
В подобных случаях требуется какой-то способ, гарантирующий, что в производном классе действительно будут переопределены все необходимые методы. И такой способ в С№ имеется. Он состоит в использовании абстрактного метода. Абстрактный метод создается с помощью указываемого модификатора типа а)эяггасг. У абстрактного метода отсутствует тело, и поэтому он не реализуется в базовом классе. Это означает, что он должен быть переопределен в производном классе, поскольку его вариант из базового класса просто непригоден для использования. Нетрудно догадаться, что абстрактный метод автоматически становится виртуальным и не требует указания модификатора чъггпа1.
В действительности совместное использование модификаторов чъггиа1 и а)эяггасс считается ошибкой. Для определения абстрактного метода служит приведенная ниже общая форма. аоясгасг тип имя(список параметроМ; Как видите, у абстрактного метода отсутствует тело. Модификатор а)эяггасг может применяться только в методах экземпляра, но не в статических методах (ягаг1с).
Абстрактными могут быть также индексаторы и свойства. Класс, содержащий один или более абстрактный метод, должен быть также объявлен как абстрактный, для чего перед его объявлением с1аяя указывается модификатор аьяггасг. А поскольку реализация абстрактного класса не определяется полностью, то Глава 1(. Наследование 351 у него не может быть объектов.
Следовательно, попытка создать объект абстрактного класса с помошью оператора пеы приведет к ошибке во время компиляции. Когда производный класса наследует абстрактный класс, в нем должны быть реализованы все абстрактные методы базового класса. В противном случае производный класс должен быть также определен как аЬзсгасс. Таким образом, атрибут аЬзсгасс наследуется до тех пор, пока не будет достигнута полная реализация класса. Используя абстрактный класс, мы можем усовершенствовать рассматривавшийся ранее класс тиоО5ьаре. Для неопределенной двумерной фигуры понятие плошади не имеет никакого смысла, поэтому в приведенном ниже варианте класса ТиоОЯЬаре метод Лгеа И и сам класс тиоОЯьаре объявляются как аьзггасг.
Это, конечно, означает, что во всех классах, производных от класса ТИООЯЬаре, должен быть переопределен метод Нгеа () . // Создать абстрактный класс. пз1пд Яузселы аЬзсгасс с1азз ТыоОЯЬаре ( СаиЫе ргъ игсспл босЫе ргь Ье1ЧЬГл // Конструктор, используемый по умолчанию. рпЫТс ТиоОЯЬаре() ( Иьбсп = Не1ЧЬГ = О.ол паве = "пп11"; ) // Параметризированный конструктор. рпЫТс ТноОЯЬаре(бопЫе н, бопЫе Ь, зсг1пд и) ( Игбсп = и; Не1ЧЬГ = Ь! паве = гы ) // Сконструировать объект равной ширины и высоты. рпЬ11с ТиоОЯЬаре(бопЬ1е х, асггпд и) ( И1НГЬ = Не1ЧЬГ = х; паве = и; ) // Сконструировать копию объекта Тиоо5Ьаре.
рпЫ1с Тиоозьаре(ТиоОЯЬаре оЫ ( ИгбГЬ = оЬ.ИТбГЬ) Не1ЧЬГ = оЬ.НегдЬГ) паве = оЬ.паве; ) // Свойства ширины и высоты объекта. рпЫгс с(опЫе И1с)гЬ ( Чес ( геспгп ргг и1бГЬ; ) аес ( ргъ нгсГЬ = ча1пе < 0 ". -ча1пе : ча1ие; ) ) рпЫгс с(опЫе Не1ЧЬГ ( 352 Часть(. Язык С() оес ( геаогп ргг Ьегдпа) ) веа ( ргг Ьегяпа = ча1ое < 0 2 -ча1ое т ча1ое) ) рпь1гс вгг1по паве ( оегт вес) роЬ11с чозб 5поиПШв() ( Сопво1е.ыг1ае11пе("Ширина и высота равны " + ИТИГЬ + " и " т Не1яьг)т // Теперь метод Агеа() является абстрактным. рпб11с апвсгаас бообзе Агеа(); ) // Клаас для треугольников, производный от класса Тнобзпаре.