частина 13

Управляти великою кількістю розрізнених класів досить складно. З цією проблемою можна впоратися шляхом упорядкування та ранжирування класів. тобто об'єднуючи загальні для декількох класів властивості в одному класі і використовуючи його в якості базового. Цю можливість надає механізм успадкування.
Спадкування застосовується для таких взаємопов'язаних цілей.
1) виключення з програми повторюваних фрагментів коду;
2) спрощення модифікації програми;
3) спрощення створення нових програм на основі існуючих.
Крім цього. спадкування є єдиною можливістю використовувати об'єкти. вихідний код яких недоступний. але в які потрібно внести зміни.
Крім механізму наслідування в даному розділі ми розглянемо такі важливі поняття ООП як поліморфізм і інкапсуляцію. які також беруть участь у формуванні ієрархії класів.
Згадаймо синтаксис класу.
[Атрибути] [специфікатор] class ім'я _ класу [: предок]
При описі класу ім'я його предка записується в заголовку класу після двокрапки. Клас. який успадковується. називається базовим. Клас. який успадковує. називається похідним. Похідний клас успадковує всі змінні. методи. властивості. оператори і індексатори. визначені в базовому класі. Крім того. в похідний клас можуть бути додані унікальні елементи. або перевизначені існуючі. Якщо ім'я предка явно не вказано. то предком вважається базовий клас всіх типів даних в мові С #, т. е. тип про bject.
Розглянемо спадкування класів на прикладі геометричних фігур. В якості базового класу створимо клас PointPlane (точка на площині), а в якості похідного класу від
PointPlane - клас PointSpace (точка в просторі):
using System; namespace MyProgram
public class PointPlane // Базовий клас - точка на площині
public int x; public int y; public void Show ()
Console.WriteLine ( "(,)", x, y);
int y = (int) ob; // unboxing Console.WriteLine ( "y =", y);
Результат роботи фрагмента програми.
У нашому випадку допустимої буде наступна команда.
PointPlane pointS = new PointSpace (1,2,3);
Більш того. т. к. тип object є базовим для всіх типів даних. стає допустимої наступна послідовність команд.

object ob1 = new PointPlane (4, 5); object ob2 = new PointSpace (6, 7, 8);
Помилка виникне при спробі звернутися до методу Show. Наприклад. команда
замість очікуваного (1, 2, 3) виведе нас (1, 2). А команда.
взагалі не зможе виконатися. т. к. для класу object метод Show не визначений.
Давайте розберемося. чому замість (1, 2, 3) ми отримали (1, 2). Як уже згадувалося раніше. всі методи класу знаходяться в пам'яті в єдиному екземплярі і використовуються всіма об'єктами одного класу спільно. Це рішення цілком логічно. так як якщо у нас є 1000 об'єктів типу PointPlane, то 1000 разів дублювати код методу Show немає ніякого сенсу.
В результаті. у випадку з привласненням посиланням на клас PointPlane об'єкта PointSpace виникає конфлікт між двома таблицями методів - базового класу і класу - нащадка.
Так як нащадків у базового класу може бути багато (в тому числі нащадків третього і більш високих рівнів) і пошук по всіх можливих таблиць методів є досить тривалим процесом. компілятор вважає. що. якщо не можуть передбачати протилежне. таблиця. в якій проводиться пошук методу. визначається типом посилання на об'єкт. Для того щоб явно вказати на необхідність пошуку потрібного методу за типом об'єкта. а не посилання. використовуються специфікатор virtual і abstract, а позначені ними методи прийнято називати віртуальними й абстрактними відповідно.
Віртуальний метод - це метод. який оголошений в базовому класі з використанням ключового слова virtual, а потім перевизначений у похідному класі за допомогою ключового слова override. При цьому. якщо реалізовано успадкування. в тому числі і багаторівневе. то кожен похідний клас може мати свою власну версію віртуального методу.
Модифікуємо методи Show в класах PointPlane і PointSpace з урахуванням сказаного.
public virtual void Show () // в класі PointPlane
Console.WriteLine ( "(,)", x, y);
public override void Show () // в класі PointSpace
Console.WriteLine ( "(,,)", x, y, z);
Тепер розглянемо наступний фрагмент програми.
PointPlane [] array = new PointPlane [6]; array [0] = new PointPlane ();
array [1] = new PointPlane (1);

array [2] = new PointPlane (2, 3); array [3] = new PointSpace (); array [4] = new PointSpace (4); array [5] = new PointSpace (5, 6, 7); foreach (PointPlane item in array)
Результат роботи фрагмента програми.
(0, 0, 0) (4, 4, 4) (5, 6, 7)
Таким чином. завдяки поліморфізму. через кількість посилань змінну базового класу можна звертатися до об'єктів різного типу. а також за допомогою одного і того ж імені виконувати різні дії.
Додайте в базовий клас PointPlane віртуальний метод Distance, що дозволяє обчислити відстань від початку координат до заданої точки. Перевизначите його в похідному класі PointSpace з урахуванням того. що точка задана в просторі. Продемонструйте роботу даного методу.
Абстрактні методи і класи
Іноді корисно створити базовий клас. визначає тільки свого роду "порожній бланк", який успадкують всі похідні класи. причому кожен з них заповнить цей "бланк" власною інформацією. Такий клас визначає структуру методів. які похідні класи повинні реалізувати. але сам клас при цьому не забезпечує реалізації цих методів. Подібна ситуація може виникнути тоді. коли клас просто не в змозі реалізувати метод. У даній ситуації розробляються абстрактні методи.
або цілі абстрактні класи.
Абстрактний метод описується за допомогою специфікатор abstract. Він не має тіла і. отже. не реалізується базовим класом. а похідні класи повинні його обов'язково перевизначити. Абстрактний метод автоматично є віртуальним і використовувати специфікатор virtual не потрібно. Більш того. якщо ви спробуєте використовувати обидва специфікатор (abstract і virtual) одночасно. то компілятор видасть повідомлення про помилку.
Якщо клас містить хоча б один абстрактний метод. його також потрібно оголосити як абстрактний. використовуючи специфікатор abstract перед class. Оскільки абстрактний клас повністю не реалізований. то неможливо створити екземпляр класу за допомогою операції

new. Наприклад. якщо клас Demo визначено як абстрактний. то спроба створити екземпляр класу Demo спричинить помилку.
Demo a = new Demo ();
Однак можна створити масив посилань. тип яких відповідає абстрактному класу.
Demo [] Ob = new Demo [5];
Якщо похідний клас успадковує абстрактний. то він повинен повністю перевизначити всі абстрактні методи базового класу. або також бути оголошений як абстрактний. Таким чином. специфікатор abstract успадковується до тих пір. поки в похідному класі не будуть реалізовані всі абстрактні методи.
Розглянемо приклад використання абстрактних методів і класів.
using System; namespace MyProgram
abstract public class Point // абстрактний клас
abstract public void Show (); abstract public double Distance ();
PointPlane і PointSpace.
using System; namespace MyProgram
public class PointPlane: Point
protected int x; protected int y;
public PointPlane (int a)