Скачиваний:
29
Добавлен:
26.03.2016
Размер:
429.43 Кб
Скачать

Вкладеність класів

Розглянутий вище приклад є прикладом вкладеності типів: у C# допускається вкладеність типів. (У цьому випадку класів як типів. Про решту подібних типів ми поки що нічого не знаємо.) При цьому вкладений тип вважається звичайним членом охоплюючого або зовнішнього класу. Таким вкладеним типом можна маніпулювати як і будь-яким іншим членом класу. Синтаксис такий:

public class OuterClass

{

public class InnerClass1 {

private class InnerClass2 {

}

Якщо внутрішній клас почне використовуватися поза класом, в якому він визначений, його, природно, треба буде вказувати з ім'ям класу, який його включає (ми це бачили в приведеному раніше прикладі на рівні об'єктів). Наприклад, можемо записати фрагмент основної програми:

static void Main()

{

OuterClass.InnerClass1 obj1 = new OuterClass.InnerClass1();

}

Як вийти правильно на елементи такого об'єкта (навіть якщо вкладеність глибша, що теж допускається), вас повідомить підкажчик середовища виконання (у нашому випадку SharpDevelop): як тільки ви наберете крапку після імені створеного об'єктf (у нашому випадку після obj1), підкажчик відкриє вікно з переліком елементів, з яких треба вибрати потрібний. Якщо ви його в цьому списку знайдете, клацніть на ньому, і він приклеїться до набраного імені об'єкта. Поставте крапку після знайденого імені. Відкриється новий список. І т. д. Якщо ви не знайдете в списку потрібного вам імені (імені підоб'єкта, імені члена підоб'єкта і т. д.), то ділянка вашої програми, в якій ви знаходитесь, не бачить потрібного вам елемента. Причина може бути в тому, що потрібний вам елемент не має атрибуту public. Або десь вкралась синтаксична помилка, яка заважає побачити елемент. Тут багато варіантів. Головне - стежити, щоб в списку обов'язково знаходилося ім'я потрібного вам елемента класу. Якщо це не так, варто розібратися чому.

Поліморфізм

Слово це означає "багато форм". Коли в класі-нащадку після спадкування його від базового класу з'являються методи базового класу буває, що виникає потреба ці базові методи переробити під потребу похідного класу. Класичний приклад: класи "Багатокутники" і "Кола". Обидва успадковані від базового класу Object, в якому визначений метод Draw() – рисувати фігуру.

Але в першому випадку треба рисувати багатокутник, а в другому - коло. А в обох класах знаходиться метод з одним і тим же ім'ям Draw(). Принцип поліморфізму забезпечує можливість різного "налаштування" методу Draw(), залишаючи незайманим ім'я методу. Тобто фактично старий метод базового класу в обох його нащадках має можливість отримати новий зміст (зрозуміло, що все це може робити компілятор). Скажемо, мовляв, а при чому тут форма? Тут треба пригадати, що слово "формувати" по-грецьки звучатиме як "морфі". З цього погляду старий метод Draw() отримає в своїх нащадках дві різні "форми". Звідси поліморфізм: багато форм.

Процес переробки старого методу в новий носить назву повторного визначення методу (method overriding). Для можливості повторного визначення треба дати якусь інформацію компілятору на цей рахунок. А інформація ця така: якщо ви в базовому класі хочете вирішити повторне визначати деякий метод в підкласах, то цей метод треба забезпечити атрибутом virtual. Отже, ми знаємо, що метод Draw() малює різні фігури, можемо припустити, що в класі-предку він помічений атрибутом virtual. Такі методи називають віртуальними. Дійсно, коли в базовому класі ви позначаєте якийсь метод атрибутом virtual, ви тим самим даєте можливість в майбутньому цей метод перевизначити в якомусь класі-нащадку зі своєю функціональністю. Тобто у момент присвоєння вами атрибуту virtual методу, інших методів поки не існує, іншими словами, вони як би існують, але не насправді, тобто віртуально. Крім того, метод, помічений ключовим словом virtual, дає можливість застосовувати цей метод за замовчуванням, яка розповсюджується на всіх нащадків базового класу. Якщо дочірній клас вирішить, він перевизначить такий метод, але він не зобов'язаний це робити. Може просто викликати цей метод з базового класу як метод за замовчуванням.

З базового класу ви дали сигнал компілятору, що такий-то метод (хай це буде Draw()) можна повторно визначати в майбутньому класі-нащадку. А як нащадок дасть сигнал компілятору, що він повторно визначає віртуальний метод? Нащадок повинен присвоїти цьому методу атрибут override. Все. Ланцюжок замкнувся. Отже в класі "Багатокутники" або в класі "Кола" метод Draw() має атрибут override. Якщо вас влаштовує функціональність віртуального методу базового класу, і ви хочете ще додати свої якісь оператори, то щоб не писати все тіло старого методу, є можливість скористатися ключовим словом base. Тоді, наприклад, звернення до методу Draw() всередині його повторного визначення виглядатиме як base.Draw(); тобто він нарисує фігуру закладеними в нього операторами в класі-батьку, а потім далі в повторно визначуваному тілі ви можете записати якісь свої оператори, які, наприклад, намальоване коло розіб'ють на три сектори.

Робота з повторного визначення методу показана в програмі лістингу 8.13. Результат роботи представлений на рис. 8.10.

Лістинг 8.13

using System;

namespace app30_overriding

{

public class A

{

int a; // Поля

int b;

// Властивості

public int A_a

{

get {return a;}

set {a=value;}

}

public int A_b

{

get {return b;}

set {b=value;}

}

// Конструктори

public A() {}

public A(int a,int b) { this.a=a; this.b=b; }

// Віртуальний метод

public virtual int M_A() { return (a + b); }

}

class B : A

{

int a;

int b;

int c; // Властивість

public int C_c

{

get {return c;}

set {c=value;}

}

// Конструктор (зазначається, який конструктор базового класу буде викликаний)

public B(int aa, int bb, int cc):base(aa, bb)

{

a=aa; b=bb; c=cc;

}

// Повторне визначення методу базового класу: він повинен поле "с" піднести до квадрату

// і додавати результат до результату старого методу (із класу А)

public override int M_A()

{

return (base.M_A() + c*c);

}

}

class Program

{

public static void Main(string[] args)

{

Console.WriteLine("Результат роботи " + "повторно визначеного методу");

B b_b = new B(5,6,7);

Console.WriteLine("1-е поле в класі-предку — {0}\n2-е поле в класі-предку — {1}",

b_b.A_a, b_b.A_b);

Console.WriteLine("Значення поля похідного класу — {0}\nРезультат повторного визначення

методу — {1}", b_b.C_c,b_b.M_A());

Console.Write("Press any key to continue... ");

Console.ReadKey(true);

}

}

}

Всі пояснення — по тексту програми.

Рис. 8.10. Результат роботи перевизначеного в класі-нащадку віртуального методу