1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 53
Текст из файла (страница 53)
Язык C#// Свойства 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 showDim() {Console.WriteLine("Ширина и высота равны " +width + " и " + height);}}// Класс треугольников, производный от TwoDShape.class Triangle : TwoDShape {string style; // Закрытый член./* Конструктор по умолчанию. Он автоматически вызываетконструктор по умолчанию класса 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 showStyle() {Console.WriteLine("Треугольник " + style);}class Shapes5 {public static void Main() {Triangle t1 = new Triangle();Triangle t2 = new Triangle("прямоугольный", 8.0, 12.0);Triangle t3 = new Triangle(4.0);t1 = t2;Console.WriteLine("Информация о t1: ");t1.showStyle();Глава 11.
Наследование289t1. showDim();Console.WriteLine("Площадь равна " + t1.area());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();}}При выполнении этой версии программы получаем такие результаты:Информация о t1:Треугольник прямоугольныйШирина и высота равны 8 и 12Площадь равна 48Информация о t2:Треугольник прямоугольныйШирина и высота равны 8 и 12Площадь равна 4 8Информация о t3:Треугольник равнобедренныйШирина и высота равны 4 и 4Площадь равна 8Рассмотрим ключевые концепции base-механизма. При задании производнымклассом base-“метода” вызывается конструктор непосредственного базового класса.
Такимобразом, ключевое слово base всегда отсылает к базовому классу, стоящему в иерархииклассов непосредственно над вызывающим классом. Это справедливо и длямногоуровневой иерархии. Чтобы передать аргументы конструктору базового класса,достаточно указать их в качестве аргументов “метода” base(). При отсутствии ключевогослова base автоматически вызывается конструктор базового класса, действующий поумолчанию.Наследование и сокрытие именПроизводный класс может определить член, имя которого совпадает с именем членабазового класса. В этом случае член базового класса становится скрытым в производномклассе. Поскольку с точки зрения формального синтаксиса языка C# эта ситуация неявляется ошибочной, компилятор выразит свое “недоумение” всего лишьпредупреждающим сообщением.
Это предупреждение должно послужить напоминанием офакте сокрытия имени. Если вы действительно собирались скрыть член базового класса, тодля предотвращения этого предупреждения перед членом производного290Часть I. Язык C#класса необходимо поставить ключевое слово new. Необходимо понимать, что эта функцияслова new совершенно отличается от его использования при создании экземпляра объекта.Рассмотрим пример сокрытия имени.// Пример сокрытия имени в связи с наследованием.using System;class A {public int i = 0;}// Создаем производный класс.class В : A {new int i; // Этот член i скрывает член i класса A.public В(int b) {i = b; // Член i в классе В.}}public void show() {Console.WriteLine("Член i в производном классе: " + i);}class NameHiding {public static void Main() {В ob = new В(2);}}ob.show();Во-первых, обратите внимание на использование ключевого слова new приобъявлении члена i в классе B.
По сути, он сообщает компилятору о том, что вы знаете, чтосоздается новая переменная с именем i, которая скрывает переменную i в базовом классеА. Если убрать слово new, компилятор сгенерирует предупреждающее сообщение.Результаты выполнения этой программы выглядят так:Член i в производном классе: 2Поскольку в классе в определяется собственная переменная экземпляра с именем i,она скрывает переменную i, определенную в классе А. Следовательно, при вызове методаshow() для объекта типа B, отображается значение переменной i, соответствующее ееопределению в классе B, а не в классе А.Использование ключевого слова base для доступа к скрытому имениСуществует вторая форма использования ключевого слова base, которая действуетподобно ссылке this, за исключением того, что ссылка base всегда указывает на базовыйкласс производного класса, в котором она используется.
В этом случае формат ее записитакой:base.членГлава 11. Наследование291Здесь в качестве элемента член можно указывать либо метод, либо переменнуюэкземпляра. Эта форма ссылки base наиболее применима в тех случаях, когда имя члена впроизводном классе скрывает член с таким же именем в базовом классе. Рассмотримследующую версию иерархии классов из предыдущего примера:// Использование ссылки base для доступа к скрытому имени.using System;class A {public int i = 0;}// Создаем производный класс.class B : A {new int i; // Эта переменная i скрывает i класса А.public B(int a, int b) {base.i = a; // Так можно обратиться к i класса А.i = b; // Переменная i в классе B.}}public void show() {// Эта инструкция отображает переменную i в классе А.Console.WriteLine("i в базовом классе: " + base.i);// Эта инструкция отображает переменную i в классе В.Console.WriteLine("i в производном классе: " + i);}class UncoverName {public static void Main() {B ob = new B(1, 2);}}ob.show();Результаты выполнения этой программы выглядят так: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. Язык C#}// Создаем производный класс.class B : A {new int i; // Эта переменная i скрывает// одноименную переменную класса A.public B(int a, int b) {base.i = a; // Так можно обратиться к// переменной i класса А.i = b; // Переменная i в классе B.}}// Этот метод скрывает метод show(), определенный в// классе А.
Обратите внимание на использование// ключевого слова new.new public void show() {base.show(); // Вызов метода show() класса А.// Отображаем значение переменной i класса B.Console.WriteLine("i в производном классе: " + i);}class UncoverName {public static void Main() {B ob = new B(1, 2);}}ob.show();Вот результаты выполнения этой программы:i в базовом классе: 1i в производном классе: 2Как видите, при вызове base.show() происходит обращение к версии методаshow(), определенной в базовом классе.Обратите внимание на то, что назначение ключевого слова new в этой программе —сообщить компилятору о том, что вы сознательно создаете в классе в новый метод с именемshow(), который скрывает метод show(), определенный в классе А.Создание многоуровневой иерархииДо сих пор мы использовали простые иерархии, состоящие только из базового ипроизводного классов.
Но можно построить иерархии, которые содержат любое количествоуровней наследования. Как упоминалось выше, один производный класс вполне допустимоиспользовать в качестве базового для другого производного класса. Например, из трехклассов (А, B и C) с может быть производным от B, который, в свою очередь, может бытьпроизводным от А.
В подобной ситуации каждый производный класс наследует содержимоевсех своих базовых классов. В данном случае класс C наследует все члены классов B и A.Чтобы понять, какую пользу можно получить от многоуровневой иерархии,рассмотрим следующую программу. В ней производный класс Triangle используется вкачестве базового для создания производного класса с именем ColorTriangle. КлассГлава 11. Наследование293ColorTriangle наследует все члены классов Triangle и TwoDShape идобавляет собственное поле color, которое содержит цвет треугольника.// Многоуровневая иерархия.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 showDim() {Console.WriteLine("Ширина и высота равны " +width + " и " + height);}// Класс треугольников, производный от класса TwoDShape.class Triangle : TwoDShape {string style; // Закрытый член./* Конструктор по умолчанию.