Учебно-методический комплекс общее количество часов 58 ч. Лекции 28 ч


Download 2.46 Mb.
bet43/91
Sana19.10.2023
Hajmi2.46 Mb.
#1709453
TuriУчебно-методический комплекс
1   ...   39   40   41   42   43   44   45   46   ...   91
Bog'liq
Язык программирования C#

9.2. Конечные классы

Если класс создается как часть большого проекта или с целью его распространения в коммерческой библиотеке бывает удобным запретить наследование от него. Необходимость в этом возникает, если класс может быть в дальнейшем изменен, в таком случае классы созданные независимыми разработчиками и наследующие от него могут престать работать или станут работать некорректно.


Для запрещения наследования от класса используется спецификатор sealed. Спецификатор sealed ставится перед ключевым словом class или перед всеми остальными спецификаторами, если они имеются. Классы с запрещенным наследованием называются конечными или sealed классами. Определение конечного класса имеет следующий формат.


sealed class имя_класса
{
// члены класса
}

Для иллюстрации использования спецификатора sealed приведем небольшой пример попытки построения иерархии на основе конечного класса.


sealed class A


{
}

class B : A


{
}
Лист. 9.5

На этот код компилятор отреагировал следующим сообщением об ошибке.


Error 1 'SEALED.B': cannot derive from sealed type 'SEALED.A'


9.3. Сокрытие имен

Отвечая на новые требования к функциональности, производный класс может по-новому использовать имена членов базового класса, определив в себе новые члены с таким же именем. Тогда одноименные члены базового класса скрываются новыми членами, то есть возникает сокрытие имен.


Такое сокрытие не является ошибочным и может быть произведено без каких-либо дополнительных манипуляций или использования дополнительных ключевых слов. Но в таком случае компилятор выдаст предупреждение о сокрытии имен.

class A
{


int i;
}

class B : A


{
double i;
}
Лист. 9.6

При компиляции этого кода будет выведено следующее предупреждение:


Warning 1 'COVER.B.i' hides inherited member 'COVER.A.i'. Use the new keyword if hiding was intended.


Чтобы избавится от предупреждений компилятора необходимо пред переопределением переменной поставить ключевое слово new.


class A
{


public int i;
}

class B : A


{
new public int i;
}
Лист. 9.7

Для доступа к сокрытым именам переменных, методов и свойств предназначено ключевое слово base, его применение аналогично использованию ключевого слова this только указывает base на базовый класс.


Проиллюстрируем использование сокрытия имен на следующей простой иерархии классов, состоящей из базового класса Person и его потомка Employee.


Сначала определим класс Person, содержащий информацию о некотором человеке. В нем содержатся имя, фамилия и дата рождения, конструктор для их инициализации и метод для их вывода на консоль.

class Person


{
public string Name, FamilyName;
public DateTime BirthDate;

public void Show()


{
Console.WriteLine("Имя: " + Name);
Console.WriteLine("Фамилия: " + FamilyName);
Console.WriteLine("Дата рождения: {0:D}", BirthDate);
}

public Person(string Name, string FamilyName, DateTime BirthDate)


{
this.Name = Name;
this.FamilyName = FamilyName;
this.BirthDate = BirthDate;
}
}
Лист. 9.8

Сначала в этом листинге объявляются три переменные Name, FamilyName и BirthDate, содержащие соответственно Имя, Фамилию и возраст. Для инициализации этих переменных определен конструктор, ожидающий значения каждой из них. А для их вывода определен метод Show().


Создадим класс Employee производный от класса Person. Этот класс должен добавить к функциональности класса Person поля, хранящие текущую должность и зарплату сотрудника и соответственно конструктор для инициализации этих полей и метод для вывода.


class Employee : Person


{
public string Position;
public decimal Salary;

new public void Show()


{
base.Show();
Console.WriteLine("Должность: " + Position);
Console.WriteLine("Зарплата: " + Salary);
}

public Employee(string Name, string FamilyName, DateTime BirthDate,


string Position, decimal Salary)
: base (Name, FamilyName, BirthDate)
{
this.Position = Position;
this.Salary = Salary;
}
}
Лист. 9.9

Определенный в листинге класс Employee является наследником класса Person. Он добавляет к полям базового класса два поля Position и Salary для хранения должности и зарплаты сотрудника соответственно. В классе определяется конструктор, вызывающий конструктор базового класса для инициализации унаследованных полей, и затем инициализирующий собственные поля. Особое внимание следует обратить на метод Show(), выводящий поля класса. Этот метод перекрывает одноименный метод базового класса, чтобы клиентам класса было удобнее выводить его содержимое. Но метод вызывает и одноименный с ним метод базового класса, используя ключевое слово base.


Создадим теперь программу для тестирования получившейся иерархии классов. Протестируем в ней класс потомок Employee.


class Program


{
static void Main(string[] args)
{
Employee[] Employees = new Employee[2];

Employees[0] = new Employee("Степан", "Степанов", new


DateTime(1960, 1, 3), "Старший сантехник", 1000000);
Employees[1] = new Employee("Иван", "Иванов",
new DateTime(1967, 1, 3),"Маладший сантехник", 100000);

for (int i = 0; i < 2; i++)


Employees[i].Show();
}
}
Лист. 9.10

В этом листинге создается массив Employees содержащий два объекта класса Employee, затем эти объекты отдельно инициализируются, а далее в цикле выводятся.


Имя: Степан


Фамилия: СтепановДата рождения: 3 января 1960 г.
Должность: Старший сантехник
Зарплата: 1000000
Имя: Иван
Фамилия: Иванов
Дата рождения: 3 января 1967 г.
Должность: Маладший сантехник
Зарплата: 100000
Вывод 9.2

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


При присвоении объекта производного класса объекту базового, объект базового класса будет обращаться именно к своим членам, даже если они были переопределены в производном классе.
Создадим иерархию, состоящую из двух классов: класса треугольника Triangle и класса четырехугольника Quadrangle. В этой иерархии класс Quadrangle наследует от Triangle. И переопределяет в нем метод Draw(), отвечающий за вывод фигуры на дисплей.

class Point


{
public double X, Y;

public Point(double X, double Y)


{
this.X = X;
this.Y = Y;
}
}

class Triangle


{
public Point A, B, C;

public void Draw()


{
Console.WriteLine();
Console.WriteLine("Треугольник");
Console.WriteLine("A({0},{0})", A.X, A.Y);
Console.WriteLine("B({0},{0})", B.X, B.Y);
Console.WriteLine("C({0},{0})", C.X, C.Y);
}

public Triangle(double X1, double Y1, double X2, double Y2, double X3,


double Y3)
{
A = new Point(X1, Y1);
B = new Point(X2, Y2);
C = new Point(X3, Y3);
}
}

class Quadrangle : Triangle


{
public Point D;

new public void Draw()


{
Console.WriteLine();
Console.WriteLine("Четырехугольник");
Console.WriteLine("A({0},{0})", A.X, A.Y);
Console.WriteLine("B({0},{0})", B.X, B.Y);
Console.WriteLine("C({0},{0})", C.X, C.Y);
Console.WriteLine("D({0},{0})", D.X, D.Y);
}

public Quadrangle(double X1, double Y1, double X2, double Y2, double X3,


double Y3, double X4, double Y4)
: base(X1, Y1, X2, Y2, X3, Y3)
{
D = new Point(X4, Y4);
}
}
Лист. 9.11

Опишем теперь работу приведенного листинга.


Сначала мы определяем класс Point, этот класс не участвует в иерархии, а служит для хранения двух вещественных переменных X и Y, используемых для хранения координат некоторой точки на декартовой координатной плоскости.
Затем мы создаем базовый в нашей иерархии класс Triangle. В нем объявляем три переменные A, B и C типа Point для хранения координат трех точек, образующих треугольник на декартовой плоскости. Для инициализации этих точек в класс добавлен конструктор, а для их вывода на экран метод Draw().
В завершении листинга мы создаем производный от класса Triangle класс Quadrangle, этот класс к полям уже имеющимся у Triangle добавляет еще одно поле D, для хранения четвертой точки четырехугольника. Для инициализации добавленной и унаследованной точки используется конструктор класса, вызывающий для инициализации первых трех точек конструктор базового класса, а для вывода всех точек образующий четырехугольник используется метод Draw(), переопределяющий метод базового класса.

Напишем теперь программу для тестирования созданной иерархии.


class Program


{
static void Main(string[] args)
{
Quadrangle Quadr = new Quadrangle(1, 1, 2, 2, 3, 3, 4, 4);
Triangle Tri = new Triangle(11, 11, 12, 12, 13, 13);

Quadr.Draw();


Tri.Draw();

Tri = Quadr;


Tri.Draw();
}
}
Лист. 9.11

В-первых четырех строках листинга не происходит ничего особенного. Сначала создаются


два объекта: объект Quadr класса Quadrangle и объект Tri класса Triangle, затем вызывается метод Draw() каждого из объектов.
Самая соль состоит в последних двух операция программы. Сначала объекту Tri базового класса Triangle присваивается объект Quadr производного класса Quadrangle. Затем вызывается метод Draw() объекта Tri, он выведет те поля, которые уже были при создании
объекта Quadr, но только те из них, которые доступны в классе Triangle.
Вывод программы.

Четырехугольник


A(1,1)
B(2,2)
C(3,3)
D(4,4)

Треугольник


A(11,11)
B(12,12)
C(13,13)

Треугольник


A(1,1)
B(2,2)
C(3,3)
Вывод 9.3



Download 2.46 Mb.

Do'stlaringiz bilan baham:
1   ...   39   40   41   42   43   44   45   46   ...   91




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©fayllar.org 2024
ma'muriyatiga murojaat qiling