Підрозділ 3.1
Класи та об'єкти
Вводить поняття класу й об'єкта, поля, методи, створення екземплярів, доступ через крапку та базову організацію власних типів.
3.1. Класи та об'єкти
Проблема без класів
Розглянемо типову задачу: зберегти інформацію про пацієнтів клініки та вивести її на екран. На перший погляд здається, що достатньо кількох змінних — ім'я, телефон, вік. Але що відбувається, коли пацієнтів стає більше одного?
Уже з двома пацієнтами — шість змінних. Із десятьма — шістдесят. Їх легко переплутати: ніщо не заважає випадково записати вік у змінну телефону. Передати набір пов'язаних змінних у метод — окремий виклик, адже доведеться перераховувати кожну з них як окремий параметр. Зберегти всіх пацієнтів у масив — взагалі неможливо, бо масив містить лише один тип.
Саме тут у гру вступають класи — механізм, що дозволяє визначати власні типи даних, об'єднуючи пов'язані поля й методи роботи з ними в єдину структуру.
Клас і об'єкт
В основі об'єктно-орієнтованого програмування лежать два нерозривно пов'язані поняття.
Клас — це опис нового типу даних: він визначає, які поля (дані) та методи (поведінка) матиме кожен представник цього типу. Клас — це лише шаблон, він не займає пам'яті під конкретні дані.
Об'єкт — конкретний екземпляр класу, створений у пам'яті під час виконання програми. Кожен об'єкт зберігає власні значення полів, незалежні від інших об'єктів того самого класу.
Аналогія з повсякденного життя: клас — це бланк анкети пацієнта, однаковий для всіх. Заповнена анкета конкретної людини — це об'єкт. З одного бланку можна заповнити тисячу анкет, і кожна міститиме свої унікальні дані.
Важливо також усвідомити: у системі типів C# клас — це повноцінний тип, нарівні з int, string чи bool. Після оголошення class Patient компілятор знає тип Patient і дозволяє використовувати його скрізь, де дозволено будь-який інший тип: як тип змінної, параметра методу, елемента масиву, повернення функції.

Оголошення класу
Клас оголошується ключовим словом class, за яким слідує ім'я та тіло у фігурних дужках:
class Patient
{
// поля та методи
}Ім'я класу записується у стилі PascalCase — кожне слово з великої літери: Patient, DoctorManager, AppointmentStatus. Це загальноприйнята конвенція для типів у C#.
Усередині фігурних дужок розміщується вміст класу — поля, методи, константи та інші члени.
Поля
Поля — це змінні, оголошені безпосередньо в тілі класу. Вони зберігають стан кожного конкретного об'єкта і доступні з будь-якого методу цього класу без передачі через параметри.
class Patient
{
public string Name = "Невідомий";
public string Phone = "";
public int Age;
}Модифікатор public означає, що поле доступне ззовні класу — з будь-якого місця програми, де є посилання на об'єкт. Це найпростіший підхід, достатній для початку; у пізніших розділах ми побачимо, чому в реальних проєктах поля роблять private і захищають їх через властивості.
Якщо поле не ініціалізоване явно, воно отримує значення за замовчуванням відповідно до свого типу. Це не випадковий сміч із пам'яті, а гарантована поведінка середовища виконання .NET:
Зверніть увагу: прямий публічний доступ до полів (
patient.Name = "...") — це найпростіший підхід для навчального прикладу. У реальних проєктах поля роблятьprivateі надають доступ через властивості з валідацією. Ми прийдемо до цього в розділі про інкапсуляцію.
Методи
Методи визначають поведінку об'єкта — що він вміє робити. На відміну від звичайних функцій, методи класу мають прямий доступ до всіх його полів без передачі через параметри: компілятор неявно передає посилання на поточний об'єкт у кожен виклик методу.
Усередині методу поля Name, Phone, Age доступні напряму — без будь-якого префікса. Явно вказати, що йдеться про поле саме цього об'єкта, а не про локальну змінну, можна через ключове слово this:
public void Display()
{
// this.Name та Name — одне й те саме,
// але this корисний коли параметр має те ж ім'я що й поле
Console.WriteLine($"Пацієнт: {this.Name}, вік: {this.Age}");
}Створення об'єкта
Оголошення класу — це лише опис. Щоб отримати реальний об'єкт у пам'яті, використовується оператор new:
Patient patient1 = new Patient();У цьому рядку відбуваються три речі:
new Patient()виділяє блок пам'яті в купі (heap) та ініціалізує всі поля значеннями за замовчуванням або початковими значеннями.- Викликається конструктор — спеціальний метод ініціалізації (наразі автоматичний, без параметрів).
- Змінна
patient1отримує посилання — адресу розміщення об'єкта в пам'яті.
Починаючи з C# 9, якщо тип змінної вже вказаний зліва, його можна не повторювати в new:
Patient patient1 = new(); // рівнозначно new Patient()Точкова нотація
Доступ до полів і методів об'єкта здійснюється через крапку (dot notation): спочатку ім'я змінної, потім крапка, потім ім'я члена.
Об'єкти у пам'яті
Щоб правильно розуміти поведінку об'єктів, важливо мати уявлення про те, де і як вони зберігаються.
Стек (stack) — швидка область пам'яті для локальних змінних і параметрів методів. Стек працює за принципом LIFO: коли метод завершується, всі його локальні змінні автоматично зникають. Змінна-об'єкт (Patient patient1) живе саме тут, але вона зберігає не самі дані, а посилання — адресу у пам'яті, де реально знаходиться об'єкт.
Купа (heap) — велика область пам'яті для довгоживучих об'єктів. Оператор new завжди виділяє пам'ять у купі. Об'єкти живуть у купі до тих пір, поки на них є хоча б одне посилання; коли всі посилання зникають, збирач сміття (Garbage Collector) автоматично звільняє пам'ять.

Це пояснює два важливі факти:
- Об'єкт є доступним з будь-якого місця програми — достатньо мати посилання на нього.
- Локальна змінна може зникнути, але об'єкт у купі залишиться, якщо на нього ще посилаються.
Кілька об'єктів
Кожен об'єкт у купі незалежний: він зберігає власний набір значень полів. Зміна полів одного об'єкта жодним чином не впливає на інший.
Дві змінні — один об'єкт
Оскільки змінна зберігає посилання, а не сам об'єкт, можливо присвоїти це посилання іншій змінній. У результаті обидві змінні вказуватимуть на той самий об'єкт у купі — і зміна через одну змінну буде видна через іншу.

Це фундаментальна відмінність між посилальними типами (class) і типами-значеннями (int, bool, struct). При присвоєнні int b = a створюється незалежна копія числа. При присвоєнні Patient b = a копіюється лише посилання, сам об'єкт залишається одним.
null — відсутнє посилання
Змінна посилального типу, яка не вказує на жоден об'єкт, містить спеціальне значення null. Це не нуль, не порожній рядок і не пустий об'єкт — це явна відсутність посилання.
Спроба викликати метод або звернутися до поля через null-посилання призводить до NullReferenceException — одного з найпоширеніших винятків у програмуванні. Саме тому перевірка на null перед використанням — обов'язкова звичка.
Масив об'єктів
Оскільки Patient — це повноцінний тип, об'єкти можна зберігати у масиві так само, як числа чи рядки. Масив Patient[] зберігає посилання на об'єкти, тому кожен елемент спочатку дорівнює null і потребує ініціалізації.
Саме цю структуру — клас і масив об'єктів — ми будемо розвивати впродовж усього курсу: додаватимемо методи пошуку, сортування, фільтрації, поступово ускладнюючи систему в міру вивчення нових інструментів мови.
Константи класу
Константи зберігають значення, що не змінюється і логічно належить всьому класу, а не конкретному об'єкту. Оголошуються з ключовим словом const, значення присвоюється лише один раз — при оголошенні.
Звернення до константи здійснюється через ім'я класу: Patient.MaxNameLength — а не через екземпляр. Це логічно: константа не залежить від конкретного об'єкта, вона є частиною самого опису типу.
Клас в окремому файлі
У реальних проєктах кожен клас розміщується у власному файлі, ім'я якого збігається з ім'ям класу: Patient.cs. Це стандартна конвенція, що спрощує навігацію у великих кодових базах.
Patient.cs
class Patient
{
public string Name = "Невідомий";
public string Phone = "";
public int Age;
public void Display()
{
Console.WriteLine($"Пацієнт: {Name}, тел: {Phone}, вік: {Age}");
}
public string GetShortInfo()
{
return $"{Name} ({Age} р.)";
}
}Program.cs
Patient p = new Patient();
p.Name = "Олена Коваль";
p.Phone = "0501234567";
p.Age = 32;
p.Display();Обидва файли належать одному проєкту — компілятор C# автоматично бачить усі класи в усіх файлах проєкту без жодного import, include чи require. Достатньо, щоб файли знаходились у тій самій директорії проєкту або підключеній до нього.
Підсумок
| Поняття | Що це |
|---|---|
class |
Шаблон нового типу з полями і методами |
| Поле | Змінна на рівні класу, зберігає стан об'єкта |
| Метод | Функція, що визначає поведінку; має доступ до полів |
new |
Виділяє пам'ять у heap, повертає посилання |
public |
Поле або метод доступні ззовні класу |
this |
Явне посилання на поточний об'єкт усередині методу |
const |
Значення належить класу, незмінне; доступ через ім'я класу |
null |
Посилання, що не вказує на жоден об'єкт |
| Stack | Зберігає локальні змінні і посилання |
| Heap | Зберігає самі об'єкти; пам'ять звільняє GC |
У наступному параграфі ми розглянемо конструктори — спеціальні методи, що дозволяють задавати початкові значення полів безпосередньо в момент створення об'єкта через new, а не після.