Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Domnin_Lab_9-12 / ЛАБ_10_C# / Виртуальные_методы_и_абстрактные классы

.doc
Скачиваний:
16
Добавлен:
02.02.2015
Размер:
159.74 Кб
Скачать

Лаб. раб. №10

ВИРТУАЛЬНЫЕ МЕТОДЫ И АБСТРАКТНЫЕ КЛАССЫ

Виртуальный метод (VM) – метод, объявленный в базовом классе с применением ключегого слова virtual и переопределенный во множестве производных классов, каждый из которых имеет свою версию виртуального метода. Вызов соответствующего виртуального метода выполняется ссылкой на базовый класс и определяется типом объекта, на который указывает эта ссылка. Обращение к функции выполняется во время выпошнения программы, динамически. Тип объекта ( а не тип ссылки ), на который указывает ссылка, определяет выбор выполняемого виртуального метода. Процесс переопределения виртуального метода в производном классе (замещенного метода) сопровождается применением модификатора override. При этом:

- сигнатуры VM и метода-заменителя должны совпадать;

- не допускется определять VM как static или abstract.

Переопределение VM организует динамическую диспетчеризацию методов (динамический полиморфизм) – вызова переопределенного метода во время выполнения программы, а не при компиляции. Ниже приводится пример программы динамического полиморфизма.

1

// Виртуальные методы и их переопределение

using System;

class Base {

// Создание VM в базовом классе

public virtual void dfa()

{

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

}

}

// Первый производный класс D1

class D1 : Base {

// Переопределение метода dfa()в классе D1.

public override void dfa()

{

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

}

}

// Второй производный класс D2

class D2 : Base {

// Переопределение метода dfa()в классе D2.

public override void dfa()

{

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

}

}

// Третий производный класс D3

class D3 : Base {

// Переопределение метода dfa()в классе D3.

public override void dfa()

{

Console.WriteLine(" Метод dfa() в классе D3 \n\n\n ");

}

}

class Overridedfa

{

public static void Main()

{

Base baseOb = new Base();

D1 dOb1 = new D1();

D2 dOb2 = new D2();

D3 dOb3 = new D3();

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

baseRef = baseOb;

baseRef.dfa();

baseRef = dOb1;

baseRef.dfa();

baseRef = dOb2;

baseRef.dfa();

baseRef = dOb3;

baseRef.dfa();

}

}

Рис 1 Работа с виртуальными методами и их переопределение

Результаты работы программы

1 Создаются базовый класс Base и три производных.

2 В классе Base объявлен метод dfa(), который переопределяется производными классами.

3 В методе Main() объявлены объекты типа Base, D1, D2, D3 и ссылка baseRef типа Base.

4 Поочередно присваиваются ссылке baseRef ссылки на объект каждого типа и по ссылке baseRef вызывается соответствующий метод dfa(). Как следует нужная версия dfa() определяется типом объекта, адресуемого в момент вызова.

Рассмотрим ситуацию, когда виртуальный метод можно не переопределять – используется версия, определеная в базовом классе

№ 2

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

using System;

class Base

{ // Создание VM в базовом классе

public virtual void dfa()

{ Console.WriteLine("\n Метод dfa() в классе Base ");

}

}

// Первый производный класс D1

class D1 : Base

{ // Переопределение метода dfa()в классе D1.

public override void dfa()

{ Console.WriteLine(" Метод dfa() в классе D1 ");

}

}

// Второй производный класс D2

class D2 : Base

{

// Этот производный класс D2 не переопределяет метод dfa().

}

// Третий производный класс D3

class D3 : Base

{

// Переопределение метода dfa()в классе D3.

public override void dfa()

{

Console.WriteLine(" Метод dfa() в классе D3 \n\n\n ");

}

}

class NoOverridedfa1

{

public static void Main()

{

Base baseOb = new Base();

D1 dOb1 = new D1();

D2 dOb2 = new D2();

D3 dOb3 = new D3();

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

baseRef = baseOb;

baseRef.dfa();

baseRef = dOb1;

baseRef.dfa();

baseRef = dOb2;

baseRef.dfa(); // Вызывает метод класса Base.

baseRef = dOb3;

baseRef.dfa();

}

}

Рис 2 Виртуальный метод не переопределеи, используется метод

базового класса

Из текста программы и приведенных результатов (рис 2) видно, что класс D2 не переопределяет метод dfa() и требуемый метод dfa() вызывается из базового класса Base.

К особому случаю относится отсутствие переопределения при многоуровневой иерархии. Ниже рассматривается такой пример.

3

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

using System;

class Base

{

// Создание VM в базовом классе

public virtual void dfa()

{

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

}

}

// Первый производный класс D1

class D1 : Base

{

// Переопределение метода dfa()в классе D1.

public override void dfa()

{

Console.WriteLine("\n Метод dfa() в классе D1 \n\n\n ");

}

}

// Второй производный класс D2

class D2 : D1

{

// Этот производный класс D2 не переопределяет метод dfa().

}

// Третий производный класс D3

class D3 : D2

{

// Этот производный класс D3 также не переопределяет метод dfa()

}

class NoOverridedfa2

{

public static void Main()

{

D3 dOb = new D3();

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

baseRef = dOb;

baseRef.dfa(); // Вызывает метод dfa() из класса D1

}

}

Рис 3 Отсутствие переопределения при многоуровневой иерархии

Как видно из текста программы и приведенных результатов (рис 3) при отсутствии переопределений для ряда классов в многоуровневой иерархии выполняется метод dfa() ближайшего класса, который переопределяет виртуальный метод.

Также можно модифицировать свойства с помощью ключевого слова virtual, а затем переопределять с помощью ключевого слова override.

Ниже рассматривается пример программы, которая содержит несколько классов, каждый из которых выполняет по своему однотипные действия (в данном случае определяется площадь геометрической фигуры). Эти однотипные действия поручены одной функции area(), которая в базовом классе объявлена как виртуальная (virtual double area()), а во всех производных класса она объявлена как переопределенная (override double area()). Тогда общую структуру текста программы можно представить так:

4

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

using System;

class TwoDShape

{ double priib; // Это private-член

double prijb; // Это private-член

string priname; // Это private-член

// Добавление конструктора по умолчанию в класс В

public TwoDShape()

{ ib = jb = 0.0;

name = "null";

}

// Конструктор класса В c параметрами(добавлено в сравнении с № 6)

public TwoDShape (double i, double j, string n)

{ ib = i;

jb = j;

name = n;

}

// Создание дополнительного объекта класса В с ib = jb, name =n

public TwoDShape(double x, string n)

{

ib = jb = x;

name = n;

}

// Создается объект из объекта

public TwoDShape(TwoDShape ob)

{

ib = ob.ib;

jb = ob.jb;

name = ob.name;

}

//

public double ib

{

get { return priib; } // get-аксессор

set { priib = value; } // set-аксессор

}

// Свойство jb

public double jb

{

get { return prijb; } // get-аксессор

set { prijb = value; } // set-аксессор

}

// Свойство name

public string name

{

get { return priname; } // get-аксессор

set { priname = value; } // set-аксессор

}

public void showb()

{

Console.WriteLine(" ib = " + ib + " и jb = " + jb );

}

//

public virtual double area()

{

Console.WriteLine("i= " + ib + "метод area() необходимо переопределить.");

return 0.0;

}

}

// Класс С производный от класса В

class Triangle : TwoDShape

{

string style; // Закрытое поле

// Конструктор по умолчанию для класса С, который

// автоматически вызывает конструктор по умолчанию класса В

public Triangle()

{ style = "null";

}

// Вызывается конструктор c параметрами базового класса В (согласно с форматом расширенного объявления)

public Triangle(double i, double j, string s)

: base(i, j, "треугольник")

{ style = s; // Инициализация поля производного класса (своего класса)

Console.WriteLine("\n style = " + style);

}

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

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

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

}

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

public Triangle(Triangle ob) : base(ob)

{ style = ob.style;

}

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

public override double area()

{

return ib * jb / 2;

}

// Метод отображает showc() тип треугольника

public void showc()

{

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

}

}

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

class Rectangle : TwoDShape {

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

public Rectangle(double i, double j) : base(i, j, "прямоугольник") {}

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

public Rectangle(double x) : base(x, "прямоугольник") { }

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

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

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

public bool isSquare() {

if(ib == jb) return true;

return false;

}

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

public override double area() {

return ib * jb;

}

}

class DShapes {

public static void Main() {

TwoDShape[] shapes = new TwoDShape[5];

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);

shapes [ 4 ] = new TwoDShape(10,20, "заготовка для фигуры");

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

Console.WriteLine(" i=" + i + " Объектом является " + shapes[i].name);

Console.WriteLine(" i=" + i + " Площадь равна " + shapes[i].area());

Console.WriteLine();

}

Console.WriteLine("\n\n\n");

}

}

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

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

РАБОТА С АБСТРАКТНЫМ КЛАССОМ

Под классом мы понимаем структуру, в которой, в программировании, размещаются реально используемые ресурсы: рабочие функции (методы), поля данных, практически все то, чем пользуются программисты. Именно классам присвоено определение “ типы данных пользователя ”. При разработке сложных программ используется множество классов, каждый из которых имеет свои методы, ориентированные на выполнение определенных задач. По сути эти классы являются производными классами. Возникла необходимость иметь дополнительный класс в качестве базового класса, ссылка на который может указывать на объект производного класса, где выполняются переопределения методов. Таким классом является абстрактный класс, который кроме общепринятых методов содержит абстрактный метод. Абстрактный метод не реализуется базовым классом, не содержит тела и автоматически является виртуальным (не использует слово virtual).Формат записи абстрактного метода имеет вид:

abstract тип имя( список параметров );

Особенности реализации абстрактного механизма:

- абстрактыми могут быть обычные методы, свойства;

- класс, имеющий хотя бы один абстрактный метод должен быть абстрактным (меть модификатор abstract перед словом class);

- для абстрактного класса экземпляры или объекты не существуют;

- попытка создать объект абстрактного класса вызывает ошибку компиляции;

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

5

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

using System;

abstract class TwoDShape

{

double priib; // Это private-член

double prijb; // Это private-член

string priname; // Это private-член

// Добавление конструктора по умолчанию в класс В

public TwoDShape()

{ ib = jb = 0.0;

name = "null";

}

// Конструктор класса В c параметрами(добавлено в сравнении с № 6)

public TwoDShape (double i, double j, string n)

{ ib = i;

jb = j;

name = n;

}

// Создание дополнительного объекта класса В с ib = jb, name =n

public TwoDShape(double x, string n)

{

ib = jb = x;

name = n;

}

// Создается объект из объекта

public TwoDShape(TwoDShape ob)

{

ib = ob.ib;

jb = ob.jb;

name = ob.name;

}

//

public double ib

{

get { return priib; } // get-аксессор

set { priib = value; } // set-аксессор

}

// Свойство jb

public double jb

{

get { return prijb; } // get-аксессор

set { prijb = value; } // set-аксессор

}

// Свойство name

public string name

{

get { return priname; } // get-аксессор

set { priname = value; } // set-аксессор

}

public void showb()

{

Console.WriteLine(" ib = " + ib + " и jb = " + jb );

}

// Tеперь метод area() абстрактный.

public abstract double area();

}

// Класс С производный от класса В

class Triangle : TwoDShape

{

string style; // Закрытое поле

// Конструктор по умолчанию для класса С, который

// автоматически вызывает конструктор по умолчанию класса В

public Triangle()

{

style = "null";

}

// Вызывается конструктор c параметрами базового класса В (согласно с форматом расширенного объявления)

public Triangle(double i, double j, string s)

: base(i, j, "треугольник")

{

style = s; // Инициализация поля производного класса (своего класса)

Console.WriteLine("\n style = " + style);

}

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

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

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

}

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

public Triangle(Triangle ob) : base(ob)

{

style = ob.style;

}

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

public override double area()

{

return ib * jb / 2;

}

// Метод отображает showc() тип треугольника

public void showc()

{

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

}

}

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

class Rectangle : TwoDShape {

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

public Rectangle(double i, double j) : base(i, j, "прямоугольник") {}

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

public Rectangle(double x) : base(x, "прямоугольник") { }

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

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

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

public bool isSquare() {

if(ib == jb) return true;

return false;

}

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

public override double area() {

return ib * jb;

}

}

class AbsShapes {

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(" i=" + i + " Объектом является " + shapes[i].name);

Console.WriteLine(" i=" + i + " Площадь равна " + shapes[i].area());

Console.WriteLine();

}

Console.WriteLine("\n\n\n");

}

}

Рис 5 Работа с абстрактным классом

Дополнительные возможности наследования

Сюда относятся:

- механизм предотвращения наследования;

- применение класса object, из которого выводятся все остальные С# _типы;

- применение типа object как обобщенного типа данных.

12