Читайте также:
|
|
Отже концепція абстрагування у програмі, створеної в об’єктно-орієнтованому стилі, реалізується через основний елемент та тип даних такої програми − клас. Для визначення класу в C# використовуються наступний синтаксис:
[атрибути] [модифікатори] class ім'я_класу [: базовий клас [,інтерфейси]] { //константи - містять незмінні значення, пов’язані із класом //поля класу - містять структурні характеристики (дані) класу //методи класу - реалізовують обчислення чи інші дії, які виконуються класом або екз. //властивості класу - визн. характеристики класу разом із способами їх чит. та запису //індексатори класу - забезп. можливість доступу до елементів класу за їх пор. номером //конструктори - реаліз.ініціал екземплярів класу чи класу в цілому //деструктор - визн.дії, які потрібно виконати перед знищенням об’єкту //операції - задють дії над об’єктами з допомогою знаків операцій //підкласи - типи, які є внутрішніми по відношенню до класу //події класу - визначають повідомлення, які може генерувати клас } |
Усі складові класу називаються його елементами або членами класу. Звертання (виклик) до констант, полів та методів класу здійснюється з допомогою операції доступу (крапки) має синтаксис:
ім'я_об'єкта.ім'я_елемента |
Стосовно атрибутів відмітимо наступне. Атрибути забезпечують ефективний спосіб зв’язування метаданих або декларативної інформації із кодом (збірками, типами, методами, властивостями і т.д.). Звернення до атрибуту може бути здійснене під час виконання програми за допомогою методу, який називається відбиттям. У C# атрибут задається шляхом вказання його імені у квадратних дужках ([]), перед оголошенням сутності (це може бути не тільки клас, але й метод класу, параметри методу тощо), до якої він відноситься.
Модифікатори слугують для зміни оголошення типів та їх членів. Наприклад модифікатор const вказує на те, що значення поля або локальної змінної не може бути зміненим.
Поля класу
Поля класу визначають структурні характеристики об’єктів класу і використовуються для зберігання значень цих характеристик. Для визначення поля класу використовується синтаксис:
[атрибути] [модифікатори] тип_даних ім'я_поля [= початкове_значення]; |
Як приклад припустимо, що в результаті об’єктної декомпозиції задачі виділено об’єкт «Раціональне число». Нагадаємо, що раціональне число можна подати у вигляді нескоротного дробу , де − ціле, а − натуральне. Виходячи із сказаного, поля класу «Раціональне число» повинні містити інформацію про структурні характеристики − значення чисельника та знаменника відповідного дробу.
class Rational { //чисельник, подається у вигляді цілого числа з діапазону від -2147483648 до 2147483647 int numerator = 0; //знаменник int denominator = 1; } |
Статичні поля. Модифікатор static використовується для оголошення члена класу, який належить всьому типу, а не конкретному об’єкту. Статичне поле створюється в єдиному екземплярі (є спільним) для всіх об’єктів даного класу (належить до даних класу). За умов доступності звертання до статичного поля поза класом, у якому воно визначене, здійснюється на рівні класу, а саме за правилом
Ім'я_класу.Ім'я_статичного_поля |
У середині класу до статичних полів звертаються просто за іменем. Найпоширенішим прикладом використання статичного поля класу є змінна, у якій зберігається кількість об’єктів даного класу. Додамо статичні поля у клас Rational.
class Rational { ... //кількість створених об'єктів типу Rational static uint Count; //кількість об'єктів типу Rational static uint count; } |
У момент створення чергового об’єкта значення цих полів повинно збільшуватися на 1, а при знищенні об’єкта повинно зменшуватися на 1 значення поля count. Цю реалізацію ми наведемо пізніше при розгляді конструкторів та деструктора.
Константи. Модифікатор const вказує на те, що значення поля або локальної змінної не може бути зміненим. Поле-константа використовується для визначення деякої константи. Ініціалізація константи відбувається в момент її оголошення. Значенням поля константи може бути будь-яке незмінюване значення, зокрема значення іншої константи. Наприклад, у класі «Раціональне число» можна визначити таку константу:
class Rational { ... //найменше можливе значення знаменника const int MinDenValue = 1; } |
Поле-константа, що міститься в класі, є неявно-статичним, тому при оголошенні констант не допускається використання модифікатору static.
readonly-поля. Модифікатор readonly застосовується тільки для полів та визначає поле, значення якого є доступним тільки для читання. На відміну від константи такі поля можуть бути ініціалізованими як при їх оголошенні, так і в спеціальному методі класу (конструкторі). Другою відмінністю є можливість присвоєння цій змінній значення, отриманого в результаті обчислення деякого виразу. Третя відмінність полягає в тому, що те саме readonly-поле у двох різних об’єктів може приймати різні значення (тобто readonly-поле є даними екземпляру). Наприклад, визначимо у нашому класі readonly-поле, яке буде унікальному номеру (ідентифікатору) об’єкта.
class Rational { ... //унікальний номер(ідентифікатор) об’єкту readonly uint ID; } |
Ініціалізація поля ID має проводитись у конструкторах, реалізацію яких ми розглянемо пізніше.
Методи класу
Методи визначають операції, які можуть виконувати об’єкти класу. Метод можна розглядати як перетворювач деяких вхідних даних у результат (вихідні дані методу). Неявним параметром є той об’єкт, який викликає даний метод. Доступ до нього можна одержати за допомогою ключового слова this. Для визначення методу класу в C# використовується синтаксис:
[атрибути] [модифікатори] тип_значення ім’я_методу (список_форм_параметрів) { тіло_методу [return значення]; } |
Методи можуть повертати значення. Тип значення, яке повертає метод визначається типом даних результату. Якщо тип значення, що повертається, не дорівнює void, то для повернення значення використовується ключове словоreturn. Крім того, ключове слово return зупиняє виконання методу. Тобто, якщо тип значення, що повертається, void, то інструкцію return без значення також можна використовувати для завершення виконання методу. Якщо ключове слово return відсутнє, то виконання методу завершиться, коли буде досягнуто кінець його блоку коду. Для повернення значень методами з типом значення, що повертається, відмінним від void необхідно обов’язково використовувати ключове слово return.
Список формальних параметрів методу – це список імен об’єктів із вказанням їх типів, які є вхідними даними методу:
[специфікатор] тип1 ім’я1, [специфікатор] тип2 ім’я2,..., [специфікатор]типN ім’яN |
Звертання до методу відбувається за допомогою його виклику, при якому на місце формальних параметрів підставляються фактичні параметри (аргументи). Типи аргументів повинні збігатися з типами даних відповідних формальних параметрів.
Статичні методи. Як і у випадку полів класу до методів може також застосовуватися модифікатор static. Виклик таких методів також здійснюється на рівні класу:
Ім'я_класу.Ім'я_методу(список_аргументів); |
Статичні методи призначені для обробки даних класу. Тобто, нестатичні поля в таких методах недоступні. Static-методи часто застосовуються в стандартних бібліотеках. Наприклад, методи ReadLine() і WriteLine() є статичними в класі Console, метод Parse – статичний у класах Int32, Float, Double.
Передача параметрів типів-значень та посилальних типів. Список формальних параметрів методу може бути порожнім. Наприклад:
class Rational { ... //метод виводу раціонального числа на екран void Display() { if (denominator == 1) { Console.WriteLine("R_{0}: {1}", ID, numerator); } else Console.WriteLine("R_{0}: {1}/{2}", ID, numerator, denominator); } } |
У випадку, коли список формальних параметрів не порожній, вхідні (фактичні) параметри передаються в метод за значенням або за посиланням[10]. За замовчуванням параметри передаються за значенням. Це означає, що в методі створюється копія даного об’єкта, і всі дії відбуваються саме з нею. При завершенні роботи методу ця копія знищується, а значення вхідного параметра залишається незмінним.
class Rational { ... //метод, що повертає найменше із двох раціональних чисел static Rational Min(Rational arg1, Rational arg2) { if (arg1.numerator * arg2.denominator < arg2.numerator * arg1.denominator) return arg1; else return arg2; } } |
Якщо у метод передається об’єкт посилального типу даних, то через посилання (адресу цього об’єкту) надається доступ до полів цього об’єкта. При цьому всі зміни, зроблені з полями об’єкта в методі, збережуться й після завершення його роботи, але значення самого посилання в методі змінити не можна. Коли необхідно змінити значення деякого параметра усередині методу, у тому числі й посилання на об’єкт, його описують як ref-параметр або out-параметр.
Для роботи з ref-параметром визначення методу та виклик методу повинні явно використовувати ключове слово ref. Слід мати на увазі, що об’єкт, який передається як ref-параметр має бути ініціалізованим.
class Rational { ... //метод, що обмінює значеннями два раціональні числа static void Reverse(ref Rational arg1, ref Rational arg2) { Rational temp = arg1; arg1 = arg2; arg2 = temp; } } |
Ключове слово out також використовується для передачі аргументів за посиланням. Але на відміну від ключового слова ref не вимагає ініціалізації змінної перед її передачею. Та незважаючи на те, що змінні, передані в якості out-аргументів можуть не ініціалізуватися перед передачею у метод, всередині методу їм обов’язково мають бути присвоєні значення (метод обов’язково повинен записати в них якесь значення) до моменту повернення результату методом. Нагадаємо, що за визначенням метод може повертати в якості результату тільки один об’єкт, тип якого визначений як тип значення, що повертається методом. Проте, нерідко виникають задачі, в яких результатом роботи методу повинні бути два або більше значень. Саме в цьому випадку і можна використати в методі формальні out-параметри.
Ключові слова ref і out по-різному обробляються під час виконання, але в режимі компіляції вони рівноцінні.
Методи зі змінною кількістю параметрів. Часто при реалізації класів виникає необхідність у методі, який здатен виконувати певну дію при різній кількості вхідних даних. Наприклад для нашого класу є потреба у методі, який би повертав найменше не тільки серед двох заданих раціональних чисел, але і трьох, чотирьох і т.д. У таких випадках говорять про метод зі змінною кількістю параметрів, які повинні мати однаковий тип даних. При виклику такого методу з його фактичних параметрів формується масив який передається далі у метод. Формальний параметр, що задає цей масив, описується ключовим словом params і повинен розташовуватися в списку параметрів методу останнім.
class Rational { ... //метод Min, що повертає найменше серед набору раціональних чисел static Rational Min(params Rational[] arg) { if (arg.Length > 0) { Rational min = arg[0]; for (int i = 1; i < arg.Length; i++) if (arg[i].numerator * min.denominator < min.numerator * arg[i].denominator) min = arg[i]; return min; } else throw new ArgumentException("Не задано жодного аргументу!"); } } |
Рекурсивні методи. Рекурсивні методи – це функції, у тілі яких міститься виклик самих себе. Звичайно їх використовують у ситуаціях, коли легко звести вихідну задачу до задачі того ж виду, але з іншими вихідними даними. Застосування рекурсивних методів вважається дуже витратним з погляду пам’яті та не підходить для розв’язання задач великої розмірності. Проте, існує багато задач, у яких рекурсивні алгоритми більш прості та дають компактні програми. Слід пам’ятати, що будь-який рекурсивний метод повинен мати обов’язкову умову виходу з рекурсії. При виконанні цієї умови керування передається в місце попередньої ітерації, звідки відбувся рекурсивний виклик або у функцію, що викликала даний метод.
Відзначимо, що рекурсивні методи можуть бути як статичними, так і методами об’єкта, тобто викликатися для екземпляра класу.
Наприклад у нашому класі Rational можна реалізувати рекурсивний метод для визначення НСД двох натуральних чисел із подальшим його використанням у методі скорочення дробу.
class Rational { ... //рекурсивний метод, що повертає НСД static int GCD(int a, int b) { //умова виходу з рекурсії if (b == 0) return a; else return GCD(b, a % b); } //метод скоррочення дробу static void Reduce(Rational arg) { int gcd = Rational.GCD(Math.Abs(arg.numerator), arg.denominator); arg.numerator /= gcd; arg.denominator /= gcd; } } |
Перевантаження методів. Іноді виникає необхідність робити однакову обробку для різних типів даних. У цьому випадку зручно визначати декілька методів з однаковим іменем для різних типів даних. Такий прийом називають перевантаженням функцій (методів). Перевантажені методи повинні мати різні списки формальних параметрів. Значення, що повертаються перевантаженими функціями також можуть різнитися, але це не повинно бути єдиною відмінністю. Такий прийом часто використовується в бібліотечних класах. Наприклад, у класі Console для виводу (друку) якого-небудь повідомлення існує 19 перевантажених методів WriteLine():
− WriteLine() – записує поточну ознаку кінця рядка в стандартний вихідний потік;
− WriteLine(double) – записує текстове представлення дійсного числа в стандартний вихідний потік;
− WriteLine(String) – записує заданий рядок у стандартний вихідний потік;
− WriteLine(String, object[]) – записує текстові представлення заданого масиву об’єктів у стандартний вихідний потік з використанням заданих відомостей про форматування і т.д.
У нашому класі Rational вже є два перевантаження методу Min().
Дата добавления: 2015-10-26; просмотров: 234 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Лістинг 2. Текст програми на мові C#, яка виводить на консоль привітання тільки тим користувачам, імена яких передаються як параметри командного рядка | | | Властивості та індексатори |