Здесь класс Derived2 не переопределяет метод who (). Поэтому при вызове метода



Who () для объекта класса Derived2 выполняется метод who (), определенный в клас-

Се Base.

Если производный класс не переопределяет виртуальный метод в случае много-

Уровневой иерархии, то будет выполнен первый переопределенный метод, который

Обнаружится при просмотре иерархической лестницы в направлении снизу вверх.

Рассмотрим пример.

/* Если производный класс не переопределяет виртуальный

метод в случае многоуровневой иерархии, будет выполнен

первый переопределенный метод, который обнаружится

при просмотре иерархической лестницы в направлении

снизу вверх. */

using System;

class Base {

// Создаем виртуальный метод в базовом классе,

public virtual void who() {

Console.WriteLine("Метод who() в классе Base");

class Derivedl : Base {

// Переопределяем метод who() в производном классе,

public override void who() {

Console.WriteLine("Метод who() в классе Derivedl");

class Derived2 : Derivedl {

// Этот класс не переопределяет метод who().

}

class Derived3 : Derived2 {

// Этот класс также не переопределяет метод who().

}

class No0verrideDemo2 {

public static void Main{) {

Derived3 dOb = new Derived3();

Base baseRef; // Ссылка на базовый класс.

baseRef = dOb;

baseRef.who(); // Вызывает метод who()

// из класса Derivedl.

Результаты выполнения этой программы таковы:

I Метод who() в классе Derivedl

Здесь класс Derived3 наследует класс Derived2, который наследует класс

Derivedl, который в свою очередь наследует класс Base. Как подтверждают результа-

ты выполнения этой программы, поскольку метод who () не переопределяется ни в

классе Derived3, ни в классе Derived2, но переопределяется в классе Derivedl, то

именно эта версия метода who () (из класса Derivedl) и выполняется, так как она

является первой обнаруженной в иерархии классов.

Еще одно замечание. Свойства также можно модифицировать с помощью ключе-

вого слова v i r t u a l , а затем переопределять с помощью ключевого слова override.

Зачем переопределять методы

Переопределение методов позволяет С# поддерживать динамический полимор-

физм. Без полиморфизма объектно-ориентированное программирование невозможно,

поскольку он позволяет исходному классу определять общие методы, которыми будут

пользоваться все производные классы, и в которых $ри этом можно будет задать соб-

ственную реализацию некоторых или всех этих методов. Переопределенные методы

представляют собой еще один способ реализации в С# аспекта полиморфизма, кото-

рый можно выразить как "один интерфейс — много методов".

Ключ (вернее, его первый "поворот") к успешному применению полиморфизма

лежит в понимании того, что базовые и производные классы образуют иерархию, ко-

торая развивается в сторону более узкой специализации. При корректном использо-

вании базовый класс предоставляет производному классу все элементы "пригодными

к употреблению", т.е. для прямого их использования. Кроме того, он определяет ме-

тоды, которые производный класс должен реализовать самостоятельно. Это делает оп-

ределение производными классами собственных методов более гибким, по-прежнему

оставляя в силе требование согласующегося интерфейса. Таким образом, сочетая на-

следование с возможностью переопределения (замещения) методов, в базовом классе

можно определить общую форму методов, которые будут использованы производны-

ми классами.

Применение виртуальных методов

Чтобы лучше почувствовать силу виртуальных методов, применим их к классу

TwoDShape. В предыдущих примерах каждый класс, выведенный из класса

TwoDShape, определяет метод с именем area (). Это наводит нас на мысль о том, не

лучше ли сделать метод вычисления площади фигуры агеа() виртуальным в классе

TwoDShape, получив возможность переопределить его в производных классах таким

образом, чтобы он вычислял площадь согласно типу конкретной геометрической фи-

гуры, которую инкапсулирует класс.

 


C#. Абстрактные и конечные методы.

Использование абстрактных классов

Иногда полезно создать базовый класс, определяющий только своего рода "пустой

бланк", который унаследуют все производные классы, причем каждый из них запол-

нит этот "бланк" собственной информацией. Такой класс определяет "суть" методов,

которые производные классы должны реализовать, но сам при этом не обеспечивает

реализации одного или нескольких методов. Подобная ситуация может возникнуть,

когда базовый класс попросту не в состоянии реализовать метод. Этот случай был

проиллюстрирован версией класса TwoDShape (из предыдущей программы), в которой

определение метода агеа() представляло собой "заглушку", поскольку в нем пло-

щадь фигуры не вычислялась и, естественно, не отображалась.

В будущем, создавая собственные библиотеки классов, вы убедитесь, что отсутст-

вие у метода четкого определения в контексте своего (базового) класса, не является

чем-то необычным. Описанную ситуацию можно обработать двумя способами. Один

из них, который продемонстрирован в предыдущем примере, — вывод предупреж-

дающего сообщения. И хотя такой подход может быть полезным в определенных об-

стоятельствах (например, при отладке программы), все же он не соответствует уровню

профессионального программирования. Существует и другой способ. Наша цель —

заставить производные классы переопределить методы, которые в базовом классе не

имеют никакого смысла. Рассмотрим класс Triangle. Им нельзя пользоваться, если

не определен метод area (). Необходимо иметь средство, благодаря которому произ-

водный класс обязательно переопределит все необходимые методы. Этим средством в

С# является абстрактный метод.

Абстрактный метод создается с помощью модификатора типа a b s t r a c t . Абстракт-

ный метод не содержит тела и, следовательно, не реализуется базовым классом. По-

этому производный класс должен его переопределить, поскольку он не может исполь-

зовать версию, предложенную в базовом классе. Нетрудно догадаться, что абстракт-

ный метод автоматически является виртуальным, поэтому и нет необходимости в

использовании модификатора v i r t u a l . Более того, совместное использование моди-

фикаторов v i r t u a l и abstract считается ошибкой.

Для объявления абстрактного метода используйте следующий формат записи.

abstract ТИП ИМЯ(список_параметров) ;

Как видите, тело абстрактного метода отсутствует. Модификатор a b s t r a c t можно

использовать только применительно к обычным, а не к static-методам. Свойства

также могут быть абстрактными.

Класс, содержащий один или несколько абстрактных методов, также должен быть

объявлен как абстрактный с помощью спецификатора abstract, который ставится

перед объявлением class. Поскольку абстрактный класс нереализуем в полном объе-

ме, невозможно создать его экземпляры, или объекты. Таким образом, попытка соз-

дать объект абстрактного класса с помощью оператора new приведет к возникновению

ошибки времени компиляции.

Если производный класс выводится из абстрактного, он может реализовать все аб-

страктные методы базового класса. В противном случае такой производный класс

также должен быть определен как абстрактный. Таким образом, атрибут abstract на-

следуется до тех пор, пока реализация класса не будет полностью достигнута.

Используя абстрактный класс, можно усовершенствовать определение класса

TwoDShape. Поскольку для не определенной заранее двумерной фигуры понятие

площади не имеет смысла, в следующей версии предыдущей программы метод

area () в классе TwoDShape объявляется как абстрактный, как, впрочем, и сам класс

TwoDShape. Безусловно, это означает, что все классы, выведенные из TwoDShape,

должны переопределить метод area ().

// Создание абстрактного класса.

using System;

abstract class TwoDShape {

double pri_width; // Закрытый член,

double pri_height; // Закрытый член,

string pri_name; // Закрытый член.

// Конструктор по умолчанию,

public TwoDShape() {

width = height = 0.0;

name = "null";

}

// Конструктор с параметрами.

public TwoDShape(double w, double h, string n) {

width = w;

height = h;

name = n;

}

// Создаем объект, у которого ширина равна высоте,

public TwoDShape(double x, string n) {

width = height = x;

name = n;

// Создаем объект из объекта,

public TwoDShape(TwoDShape ob) {

width = ob.width;

height = ob.height;

name = ob.name;

}

// Свойства width, height и name,

public double width {

get { return pri_width; }

set { pri_width = value; }

}

public double height {

get { return pri_height; }

set { pri_height = value; }

310 Часть I. Язык С#

http://openlib.org.ua/

public string name {

get { return pri__name; }

set { pri_name = value; }

}

public void showDim() {

Console.WriteLine("Ширина и высота равны " +

width + " и " + height);

}

// Теперь метод агеа() абстрактный,

public abstract double area();

}

// Класс треугольников, производный ачг класса TwoDShape.

class Triangle : TwoDShape {

string style; // Закрытый член.

// Конструктор по умолчанию,

public Triangle() {

style = "null";

}

// Конструктор с параметрами.

public Triangle(string s, double w, double h) :

base(w, h, "triangle") {

style = s;

}

// Создаем равнобедренный треугольник.

public Triangle(double x) : base(x, "треугольник") {

style = "равнобедренный";

}

// Создаем объект из объекта. ,

public Triangle(Triangle ob) : base(ob) {

style = ob.style;

}

// Переопределяем метод агеа() для класса Triangle,

public override double area() {

return width * height / 2 ;

}

// Отображаем тип треугольника,

public void showStyle() {

Console.WriteLine("Треугольник " + style);

}

}

// Класс прямоугольников, производный от класса TwoDShape.

class Rectangle : TwoDShape {

// Конструктор с параметрами.

public Rectangle(double wf double h) :

base(w, h, "прямоугольник"){ }

// Создаем квадрат.

public Rectangle (ckmfcrle x) :

base(xf "прямоугольник") { }

// Создаем объект из объекта.

public Rectangle(Rectangle ob) : base(ob) { }

// Метод возвращает значение true, если

// прямоугольник является квадратом,

public bool isSquareO {

if (width ===== height) return true;

return false;

// Переопределяем метод агеа() для класса Rectangle,

public override double area() {

return width * height;

class AbsShape {

public static void Main() {

TwoDShape [] shapes == new TwoDShape[4];

shapes[0] = new Triangle("прямоугольный", 8.0, 12.0);

shapes[1] = new Rectangle(10);

shapes[2] = new Rectangle(10, 4);

shapes[3] = new Triangle (7.0);

for(int i=0; i < shapes.Length; i++) {

Console.WriteLine("Объектом является " +

shapes[i].name);

Console.WriteLine("Площадь равна " +

shapes[i 3.area());

Console.WriteLine();

Как продемонстрировано этой программой, все производные классы должны или

переопределить метод area (), или также объявить себя абстрактными. Чтобы убе-

диться в этом, попробуйте создать производный класс, который не переопределяет

метод area (). Вы тут же (т.е. во время компиляции) получите сообщение об ошибке.

Конечно, мы можем создать объектную ссылку типа TwoDShape, что и делается в

программе. Однако теперь нельзя объявить объект типа TwoDShape. Поэтому в методе

Main () размер массива shapes сокращен до 4, и больше не создается "заготовка для

фигуры" в виде объекта класса TwoDShape.

Обратите также внимание на то, что класс TwoDShape по-прежнему включает ме-

тод showDimO, объявления которого не коснулся модификатор a b s t r a c t . Ведь впол-

не допустимо для абстрактного класса содержать конкретные (а не только абстракт-

ные) методы, которые производный класс может использовать "как есть". И только

методы, объявленные с использованием ключевого слова abstract, должны переоп-

ределяться производными классами.


Дата добавления: 2018-05-13; просмотров: 449; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!