Основи ООП_3 в С Шарп.ppt
- Количество слайдов: 27
ООП в С# (продовження) Віртуальні, перевизначені і абстрактні методи. Лекція 9 Іван Хвищун 30 жовтня 2012 року
Віртуальні, перевизначені і абстрактні методи Якщо оголошення методу екземпляра містить модифікатор virtual, метод є віртуальним. Якщо модифікатор virtual відсутній, метод є невіртуальним методом. При виклику віртуального методу тип часу виконання екземпляра, для якого здійснюється виклик, визначає фактичну реалізацію методу, що викликається. При виклику невіртуального методу визначальним чинником є тип часу компілювання екземпляра. Віртуальний метод можна перевизначити у похідному класі. Якщо оголошення методу екземпляра містить модифікатор override, метод перевизначає успадкований віртуальний метод з такою же сигнатурою. Оголошення віртуального методу визначає новий метод. Оголошення перевизначеного методу уточнює існуючий віртуальний метод, надаючи його нову реалізацію.
Абстрактним називають віртуальний метод без реалізації. Оголошення абстрактного методу здійснюється з використанням модифікатора abstract і допускається тільки в класі, оголошеному як abstract. У кожному неабстрактному похідному класі необхідно перевизначати абстрактний метод. У наступному прикладі оголошується абстрактний клас Expression, що представляє вузол дерева виразів, а також три похідних класу: Constant, Variable. Reference і Operation, які реалізують вузли дерева виразів для констант, посилань на змінних і арифметичних операцій.
using System; using System. Collections; public abstract class Expression { public abstract double Evaluate (Hashtable vars); } public class Constant: Expression { double value; public Constant (double value) { this. value = value; } public override double Evaluate (Hashtable vars) { return value; } }
Перевантаження методу дозволяє використовувати в одному класі декілька методів з однаковими іменами і різними сигнатурами. При компілюванні виклику перевантаженого методу компілятор використовує дозвіл перевантаження для визначення конкретного методу, який потрібно викликати. За допомогою дозволу перевантаження визначається метод, найбільш відповідний для заданих аргументів, або, якщо такий метод не знайдено, повертається повідомлення про помилку. У наступному прикладі показана дія дозволу перевантаження. У коментарях до кожного виклику методу Main указується, який метод фактично викликається.
class Test { static void F() { Console. Write. Line("F()"); } static void F(object x) { Console. Write. Line("F(object)"); } static void F(int x) { Console. Write. Line("F(int)"); } static void F(double x) { Console. Write. Line("F(double)"); } static void F(double x, double y) { Console. Write. Line("F(double, double)"); } static void Main() { F(); // Викликається F() F(1); // Викликається F(int) F(1. 0); // Викликається F(double) F("abc"); // Викликається F(object) F((double)1); // Викликається F(double) F((object)1); // Викликається F(object) F(1, 1); // Викликається F(double, double) } }
Конструктори У C# підтримуються конструктори екземплярів і статичні конструктори. Конструктором екземпляра є член, який реалізує дію, необхідну для ініціалізації полів екземпляра класу. Статичним конструктором є член, який реалізує дію, необхідну для ініціалізації самого класу при його першому завантаженні. Конструктор оголошується аналогічно методу без типу значення, що повертається, і має те ж ім'я, що і клас, що містить його. Оголошення конструктора, що містить модифікатор static, оголошує статичний конструктор. Кожен клас має конструктор за замовчуванням – такий конструктор, який не має аргументів і для кожного класу генерується автоматично.
Статичні конструктори мають певні обмеження: • У класі може бути визначений лише один статичний конструктор і він не повинен мати параметрів; • Він не має модифікатора доступу; • Статичний конструктор виконується лише один раз, незалежно від кількості екземплярів класу, що створюються; • Він викликається або при створенні першого екземпляру класу або при першому звертанні до статичного члена цього класу. • Він виконується раніше, за будь-який конструктор екземпляра класу.
Інакше оголошується конструктор екземпляра. Конструктори екземплярів можна перевантажувати. Виклик конструктора екземпляра здійснюється за допомогою оператора new. На відміну від інших членів, конструктори екземплярів не можуть успадковуватися. Клас містить тільки ті конструктори екземплярів, які фактично оголошені в ньому. Якщо в класі не оголошений конструктор екземплярів, то автоматично використовується порожній конструктор без параметрів.
Конструктор за замовчуванням ініціалізує поля стандартним способом залежно від їхнього типу. При потребі його можна перевизначити. Наприклад, стандартно усім полям, що мають тип int, буде присвоєно значення 0, а якщо нам для де-яких полів у процесі їхньої ініціалізації необхідні інші значення, то доведеться перевизначити конструктор за замовчуванням. Наприклад для класу kimnata полям kilkіst. Dverej та kilkist. Vikon за замовчуванням логічніше присвоювати значення не 0, а 1:
public partial class Form 1 : Form { public Form 1() { Initialize. Component(); } public class kimnata { public int kilkist. Dverej { get; set; } public int kilkist. Vikon { get; set; } public decimal ploshcha { get; set; } public kimnata () // Це перевизначений конструктор за замовчуванням { kilkist. Dverej = 1; kilkist. Vikon = 1; } }
private void button 1_Click(object sender, Event. Args e) { kimnata mykimnata = new kimnata(); // Виклик конструктора за замовчуванням int d = mykimnata. kilkist. Dverej; int v = mykimnata. kilkist. Vikon; decimal p = mykimnata. ploshcha; // Виведемо у текст мітки на формі дані про кількість дверей, вікон та площу кімнати label 1. Text = " кількість дверей=" + d. To. String() + " кількість вікон=" + v. To. String() + " Площа=" + p. To. String(); } }
Інтерфейс цього прикладу Приклад показує, що при перевизначені конструктора за замовчуванням достатньо присвоювати потрібні значення лише тим полям (властивостям), значення яких повинні відрізнятись від стандартних значень для типів за замовчуванням. Полю ploshcha у конструкторі жодного значення присвоєно не було, однак у виведеному тексті ми бачимо 0.
Процес створення об’єкта оператором new проходить у так: • спочатку виділяється пам’ять, для типів по посиланню (об’єктів) – посилання із значенням null; • потім у динамічній пам’яті для об’єктів створюється структура об’єкту з полями, які належать класу. Поля ініціалізуються значеннями за замовчуванням згідно до їхнього типу; Якщо поля класу проініціалізовані програмно, то виконується їхня ініціалізація заданими значеннями;
Якщо викликано конструктор з аргументами, то виконуються оператори тіла конструктора. Після цього посилання зв’язується із об’єктом. Як і звичайні методи, конструктори мають модифікатор доступу – private, public або protected. Клас може мати декілька спеціальних конструкторів з різними сигнатурами. Наприклад ми можемо, при створенні об’єкта кімната, відразу вказувати через параметри конструктора необхідне значення кількості вікон та дверей, висоту стелі та площу кімнати :
// Це конструктор з параметрами public kimnata(int kil. Dver, int kil. Vik, decimal vys, decimal pl) { kilkist. Dverej = kil. Dver; kilkist. Vikon = kil. Vik; ploshcha = pl; vysota = vys; } // Це ще один конструктор з параметрами public kimnata( decimal shyr, decimal dovg) { shyryna = shyr; dovgyna =dovg; ploshcha = shyr * dovg; }
Стандартний конструктор за замовчуванням автоматично створюється для всіх класів. Однак, якщо ви створили конструктор з параметрами, то цей автоматично створений конструктор стає недоступним. При потребі ви можете явно створити конструктор без параметрів і тоді він буде доступним, якщо модифікатор доступу у нього public. Якщо створити для класу єдиний конструктор з модифікатором доступу private, то для такого класу не можна буде створити жодного його екземпляру.
Окремо слід відмітити існування такого поняття, як статичний конструктор. Він має модифікатор static і застосовується для ініціалізації статичних полів, якщо така ініціалізація вимагає виконання певних операторів. Статичні конструктори мають обмеження: • У класі можна визначений лише один статичн. конструктор і він не повинен мати параметрів; • Він не має модифікатора доступу; • Статичний конструктор виконується лише один раз, незалежно від кількості екземплярів класу, що створюються; • Він викликається або при створенні першого екземпляру класу або при першому звертанні до статичного члена цього класу. • Він виконується раніше, ніж будь який конструктор екземпляра класу.
Деструктори Оскільки купа, де створюються об’єкти, має обмежений розмір то очевидно, що пам’ять відведену об’єктам, необхідно вивільняти. У VS. Net, на відміну від старих версій С++, виконання цього завдання повністю автоматизоване. Клас може, як і раніше, мати деструктор (метод для знищення екземпляру класу), але програміст позбавлений можливості напряму викликати цей метод. Синтаксис для оголошення деструктора: ~<назва класу> () { // Код методу-деструктора }
Додавати модифікатор доступу для деструктора заборонено. Деструктори використовують тоді, коли у проекті використовується некерований код або програміст хоче вплинути на процес збирання сміття при роботі з дуже великими об’єктами для звільнення ресурсів. У C# деструктор фактично є фінелайзером (finalizer) і щільно пов’язаний із спеціальним інструментом для вивільнення пам’яті, який називають збирачем сміття (Garbage Collection, або коротко GC).
Зауважимо, що GC має досить складний алгоритм для того, щоб мінімально впливаючи на швидкодію виконання основної програми вчасно вивільняти місце у купі, а також стискати її, для зменшення фрагментації ОП. Найчастіше програмісту C# не доводиться писати жодних деструкторів, хоча при необхідності для цього існують зручні засоби. Так базовий клас System. Object має метод finalize(), який можна перевизначити у класі, що розробляється, а VS має спеціальний клас для доступу до роботи GC (тип System. GC). Метод finalize() завжди автоматично викликається збирачем сміття перед вилученням відповідного об’єкту із пам’яті.
Властивості (Properties) - це синтаксичні конструкції, які призначені для забезпечення ефективної роботи з полями класу. Вони дозволяють реалізувати політику доступу до поля: (читання/записування, лише читання, лише записування, ні читання, ні записування або читання, а записування лише при першому звертанні). У C#, як і у інших мовах, добрим стилем є оголошувати поля класу закритими, а доступ до них організовувати через властивості. Загальний синтаксис властивості є такий: [<модифікатор_доступу>] <тип> <ім’я_властивості> get { <оператори для отримання значення властивості із її закритого поля. Як правило містить оператор return ім’я_поля_властивості; > } set {< оператори записування значення властивості у закрите поле. Як правило містить оператор ім’я_поля_властивості = value; }
Наприклад int age; public int Age { get { return age; } set { if ((value > 0) &&(value < 150)) age=value; else throw (new Argument. Out. Of. Range. Exception ("Age", value, "Вік людини повинен бути між 0 та 150")); } }
У наведеному прикладі якщо значення властивості Age не буде у інтервалі між 0 та 150, то з’явиться переривання по помилці. Коли властивість оголошена у якомусь класі, то клієнт класу працює з нею як з іменем поля. Це ім’я може використовуватись у виразах як ім’я змінної: Age = Age + 1; if (Age < 6) status = "дошкільня"; if (Age >= 6 && Age < 17) status = "школяр"; Якщо ви не збираєтесь писати якогось особливого коду у аксесорах get і set, і всі оператори, які у них будуть – це return у блоці get та присвоєння значення у блоці set, то можна скористатись коротким записом оголошення властивості, наприклад public string status { get; set; }
Властивості є природними розширеннями полів класу. Вони, так як і поля є поіменованими членами, які пов'язані із типами, для звернення до яких використовують однаковий синтаксис. Проте, на відміну від полів, властивості не указують місця зберігання даних. Замість цього властивості містять методи доступу до цих даних, тобто оператори, які використовуються при читанні або записі значень відповідного поля. Властивість оголошують аналогічно полю, проте оголошення властивості повинне закінчуватися не “ ; ”, а методами доступу get або set, записаними між роздільниками, – { і }. Властивість, для якої визначено обидва методи доступу get і set, називають властивістю для читання і запису. Властивість, для якої визначений тільки метод доступу get, називають властивістю тільки для читання. Властивість, для якої визначений тільки метод доступу set, називають властивістю тільки для запису.
Метод доступу get відповідає методу без параметрів, а значення яке він повертає, має тип властивості. За винятком випадків, коли властивість є кінцевим об'єктом операції присвоєння, при посиланні на властивість у виразі викликається метод доступу get для обчислення значення властивості. Метод доступу set відповідає методу з одним параметром value, що не має типу значення, що повертається. При посиланні на властивість як на кінцевий об'єкт операції присвоєння або як на операнд операторів «++» і «--» метод доступу set викликається з аргументом, який містить нове значення.
Як і у випадку з полями і методами, в C# підтримуються властивості екземплярів і статичні властивості. Властивості, оголошені з використанням модифікатора static, називаються статичними. Решта всіх властивостей називається властивостями екземплярів. Методи доступу властивості можуть бути віртуальними. Якщо оголошення властивості містить модифікатор virtual, abstract або override, відповідний тип застосовується і до його методів доступу.
Основи ООП_3 в С Шарп.ppt