Г. Шилдт - Полный справочник по C# (1160789), страница 54
Текст из файла (страница 54)
Такой подход дает классу TwoDShape свободу выбора среди возможныхспособов построения подобъектов. Более того, со временем класс TwoDShape можетрасширять свои функции, но об этом расширении ранее созданные производныеклассы не будут "знать", что предотвратит существующий код от разрушения.С помощью ключевого слова base можно вызвать конструктор любой формы, определенный в базовом классе. Реально же выполнится тот конструктор, параметры которого будут соответствовать переданным при вызове аргументам. Например, вот каквыглядят расширенные версии классов TwoDShape и T r i a n g l e , которые включаютконструкторы по умолчанию и конструкторы, принимающие один аргумент:// Добавляем в класс TwoDShape конструкторы.using System;class TwoDShape {double pri_width; // Закрытый член.double pri_height; // Закрытый член.;// Конструктор по умолчанию,public TwoDShape() {width = height = 0.0;}// Конструктор класса TwoDShape с параметрами,public TwoDShape(double w, double h) {width = w;height = h;}// Создаем объект, у которого ширина равна высоте,public TwoDShape(double x) {width = height = x;288Часть I.
Язык С#// Свойства width и height.public double width {get { return pri_width; }set { pri_width = value; }public double height {get { return pri_height; }set { pri_height = value; }}public void showDimO {Console.WriteLine("Ширина и высота равны " +'width + " и " + height);// Класс треугольников, производный от TwoDShape.class Triangle : TwoDShape {string style; // Закрытый член. Q/* Конструктор по умолчанию. Он автоматически вызываетконструктор по умолчанию класса TwoDShape.
*/public Triangle() {style = "null";// Конструктор, который принимает три аргумента,public Triangle(string s, double w, double h) : base(w, h) {style = s;// Создаем равнобедренный треугольник,public Triangle(double x) : base(x) {style = "равнобедренный";// Метод возвращает площадь треугольника,public double area() {return width * height / 2;// Отображаем тип треугольника,public void showStyleO {Console.WriteLine("Треугольник " + style);class Shapes5 {public staticTriangle tlTriangle t2Triangle t3void Main() {= new Triangle();= new Triangle("прямоугольный", 8.0, 12.0);= new Triangle(4.0);tl = t2;Console.WriteLine("Информация о tl: " ) ;tl.showStyleO ;Глава 11. Наследование289tl.showDimO ;Console.WriteLine ("Площадь равна " + tl.areaO);Console.WriteLine() ;Console.WriteLine("Информация о t2: " ) ;t2.showStyle();t2.showDim();Console.WriteLine("Площадь равна " + t2.area());Console.WriteLine() ;Console.WriteLine("Реформация о t3: " ) ;t3.showStyle();t3.showDim();Console.WriteLine("Площадь равна " + t3.area());Console.WriteLine() ;При выполнении этой версии программы получаем такие результаты:Информация о tl:Треугольник прямоугольныйШирина и высота равны 8 и 12Площадь равна 4 8Информация о t2:Треугольник прямоугольныйШирина и высота равны 8 и 12Площадь равна 4 8(Информация о t3:Треугольник равнобедренныйШирина и высота равны 4 и 4Площадь равна 8Рассмотрим ключевые концепции base-механизма.
При задании производнымклассом Ьазе-"метода" вызывается конструктор непосредственного базового класса.Таким образом, ключевое слово base всегда отсылает к базовому классу, стоящему виерархии классов непосредственно над вызывающим классом. Это справедливо и длямногоуровневой иерархии. Чтобы передать аргументы конструктору базового класса,достаточно указать их в качестве аргументов "метода" base (). При отсутствии ключевого слова base автоматически вызывается конструктор базового класса, действующий по умолчанию.Наследование и сокрытие именПроизводный класс может определить член, имя которого совпадает с именемчлена базового класса. В этом случае член базового класса становится скрытым в производном классе.
Поскольку с точки зрения формального синтаксиса языка С# этаситуация не является ошибочной, компилятор выразит свое "недоумение" всего лишьпредупреждающим сообщением. Это предупреждение должно послужить напоминанием о факте сокрытия имени. Если вы действительно собирались скрыть член базового класса, то для предотвращения этого предупреждения перед членом произвол290Часть I. Язык С#ного класса необходимо поставить ключевое слово new. Необходимо понимать, чтоэта функция слова new совершенно отличается от его использования при созданииэкземпляра объекта.Рассмотрим пример сокрытия имени.// Пример сокрытия имени в связи с наследованием.using System;class A {p u b l i c i n t i = 0;}// Создаем производный класс,class В : А {new i n t i ; // Этот член i скрывает член i класса А.public В(int b) {i = b; // Член i в классе В.public void show() {Console.WriteLine("Член i в производном классе: " + i ) ;class NameHiding {public s t a t i c void Main() {В ob = new В(2);ob.show();Во-первых, обратите внимание на использование ключевого слова new при объявлении члена i в классе в.
По сути, он сообщает компилятору о том, что вы знаете,что создается новая переменная с именем i, которая скрывает переменную i в базовом классе А. Если убрать слово new, компилятор сгенерирует предупреждающее сообщение.Результаты выполнения этой программы выглядят так:/I Член i в производном классе: 2Поскольку в классе в определяется собственная переменная экземпляра с именемi, она скрывает переменную i, определенную в классе А.
Следовательно, при вызовеметода show() для объекта типа в, отображается значение переменной i, соответствующее ее определению в классе В, а не в классе А.Использование ключевого слова base для доступа к скрытомуимениСуществует вторая форма использования ключевого слова base, которая действуетподобно ссылке t h i s , за исключением того, что ссылка base всегда указывает на базовый класс производного класса, в котором она используется. В этом случае форматее записи такой:base.членГлава 11. Наследование291Здесь в качестве элемента член можно указывать либо метод, либо переменнуюэкземпляра.
Эта форма ссылки base наиболее применима в тех случаях, когда имячлена в производном классе скрывает член с таким же именем в базовом классе. Рассмотрим следующую версию иерархии классов из предыдущего примера:// Использование ссылки base для доступа к скрытому имени.using System;class A {public i n t i = 0;}// Создаем производный класс,class В : А {new int i ; // Эта переменная i скрывает i класса А.public В(int a, int b) {base.i = а; // Так можно обратиться к i класса А.i = b; // Переменная i в классе В.}!public void show() {// Эта инструкция отображает переменную i в классе А.Console.WriteLine("i в базовом классе: " + base.i);// Эта инструкция отображает переменную i в классе В.Console.WriteLine("i в производном классе: " + i ) ;class UncoverName {public static void Main() {В ob = new В(1 f 2 ) ;ob.show();IРезультаты выполнения этой программы выглядят так:i в базовом классе: 1i в производном классе: 2Несмотря на то что переменная экземпляра i в классе В скрывает переменную i вклассе А, ссылка base позволяет получить доступ к i в базовом классе.С помощью ссылки base также можно вызывать скрытые методы.
Рассмотримпример.// Вызов скрытого метода.using System;class A {public int i = 0;// Метод show() в классе А.public void show() {'Console.WriteLine("i в базовом классе: " + i ) ;292Часть I. Язык С#// Создаем производный класс,class В : А {new int i; // Эта переменная i скрывает// одноименную переменную класса А.public В(int a, int b) {base.i - а; // Так можно обратиться к// переменной i класса А.i = b; // Переменная i в классе В.}// Этот метод скрывает метод show О , определенный в// классе А.
Обратите внимание на использование// ключевого слова new.new public void show() {base.show(); // Вузов метода show() класса А.// Отображаем значение переменной i класса В.Console,WfiteLine ("i в ЕрО|&£ВДД£Ш КДДССё: " + i) '*c l a s s UncoverNaine {public static void Main()В ob = new В(1, 2 ) ;ob.show();IВот результаты выполнения этой программы:i в базовом классе: 1i в производном классе: 2Как видите, при вызове base.show() происходит обращение к версии методаshow (), определенной в базовом классе.Обратите внимание на то, что назначение ключевого слова new в этой программе — сообщить компилятору о том, что вы сознательно создаете в классе в новый метод с именем show (), который скрывает метод show (), определенный в классе А.Создание многоуровневой иерархииДо сих пор мы использовали простые иерархии, состоящие только из базового ипроизводного классов.
Но можно построить иерархии, которые содержат любое количество уровней наследования. Как упоминалось выше, один производный класс вполне допустимо использовать в качестве базового для другого производного класса. Например, из трех классов (А, в и с) С может быть производным от в, который, в своюочередь, может быть производным от А.
В подобной ситуации каждый производныйкласс наследует содержимое всех своих базовых классов. В данном случае класс с наследует все члены классов в и А.Чтобы понять, какую пользу можно получить от многоуровневой иерархии, рассмотрим следующую программу. В ней производный класс Triangle используется вкачестве базового для создания производного класса с именем ColorTriangle. КлассГлава 11. Наследование293C o l o r T r i a n g l e наследует все члены классов T r i a n g l e и TwoDShape и добавляет собственное поле c o l o r , которое содержит цвет треугольника.// Многоуровневая иерархия.using System;class TwoDShape {double pri__width; // Закрытый член,double pri__height; // Закрытый член.// Конструктор по умолчанию,public TwoDShape() {width = height =- 0.0;}// Конструктор класса TwoDShape.public TwoDShape(double w, double h) {width = w;height = h;}// Конструктор, создающий объекты, у которых// ширина равна высоте,public TwoDShape(double x) {width = height = x;}// Свойства width и height,public double width {get { return pri_width; }set { pri_width = value; }}public double height {get { return pri__height; }set { pri_height = value; }}public void showDimO {Console.WriteLine("Ширина и высота равны " +width + " и " + height);// Класс треугольников, производный от класса TwoDShape.class Triangle : TwoDShape {string style; // Закрытый член./* Конструктор по умолчанию.