OOP Course
Сьогодні

Підрозділ 3.2

Конструктори, ініціалізатори та деконструктори

Пояснює конструктори, перевантаження, this, ланцюжки конструкторів, ініціалізатори об'єктів і деконструктори.

3.2. Конструктори, ініціалізатори та деконструктори

Створення конструкторів

У попередньому розділі об'єкти створювались через конструктор за замовчуванням, який C# генерує автоматично. Проте у реальній розробці майже завжди виникає потреба визначити власний конструктор — щоб об'єкт одразу отримував коректні початкові значення і не міг існувати в «порожньому» стані. Уявіть картку пацієнта у клінічній системі: безглуздо мати об'єкт Patient, у якого ні імені, ні віку — такий стан є семантично некоректним. Конструктор вирішує цю проблему, гарантуючи, що кожен новий об'єкт одразу містить усі необхідні дані.

На рівні коду конструктор — це метод, який має те саме ім'я, що й клас, може приймати параметри, але не має типу, що повертається (навіть void). Варто пам'ятати: щойно ви визначаєте в класі хоча б один власний конструктор, компілятор більше не генерує конструктор за замовчуванням автоматично.

Визначимо у класі Patient найпростіший конструктор без параметрів, який встановлює початкові значення:

Конструктори можуть мати модифікатори доступу. У прикладі вище конструктор оголошено з public — це дозволяє створювати об'єкти Patient з будь-якого місця програми. Якби конструктор був private, об'єкт можна було б створити лише всередині самого класу (такий прийом використовується, наприклад, у патерні Singleton).

Перевантаження конструкторів

Клас може мати кілька конструкторів — це називається перевантаженням (overloading). Компілятор розрізняє їх за кількістю та типами параметрів. Це зручно: залежно від того, яка інформація відома в момент реєстрації пацієнта, можна викликати відповідний варіант конструктора.

У прикладі визначено три конструктори — кожен приймає різну кількість параметрів. Коли ми пишемо new Patient("Іван Петренко", 45), компілятор автоматично обирає третій варіант за сигнатурою.

Починаючи з C# 9, назву типу при виклику конструктора можна опустити, якщо тип відомий з контексту:

Patient p1 = new();               // аналогічно new Patient()
Patient p2 = new("Олена");        // аналогічно new Patient("Олена")
Patient p3 = new("Іван", 45);     // аналогічно new Patient("Іван", 45)

Це скорочення особливо зручне при ініціалізації полів або колекцій, де тип вже вказано ліворуч.

Ключове слово `this`

Ключове слово this — це посилання на поточний екземпляр класу. Найчастіше воно використовується тоді, коли параметри конструктора (або методу) мають ті самі імена, що й поля класу. Без this компілятор не може однозначно зрозуміти, де поле, а де параметр.

У виразі this.name = name ліва частина (this.name) означає поле поточного об'єкта, а права (name) — параметр конструктора. Якщо б параметри мали інші назви (наприклад, n та a), ключове слово this для доступу до поля не було б обов'язковим — хоча багато стилів кодування рекомендують використовувати this явно для ясності. Крім того, через this можна звертатися до будь-якого методу або поля класу з будь-якого іншого методу.

Ланцюжок виклику конструкторів

Коли в класі є кілька конструкторів, вони нерідко дублюють одну й ту саму логіку ініціалізації. Щоб уникнути дублювання, один конструктор може викликати інший за допомогою синтаксису : this(...). Це називається ланцюжком виклику конструкторів.

Важливо розуміти порядок виконання: спочатку виконується викликаний конструктор (той, що вказаний після :), і лише потім — тіло поточного. Тобто перший конструктор викликає другий, другий — третій, і лише після виконання третього управління повертається назад по ланцюжку.

Ланцюжок виклику конструкторів Patient через this()Ланцюжок виклику конструкторів Patient через this()

Ланцюжок конструкторів особливо корисний, коли «повний» конструктор містить нетривіальну логіку валідації або ініціалізації — тоді всі спрощені конструктори просто делегують йому роботу, і ця логіка не повторюється.

Параметри за замовчуванням як альтернатива

Коли конструктори лише передають значення одне одному без додаткової логіки, увесь ланцюжок можна замінити одним конструктором з параметрами за замовчуванням. Це значно скорочує код і спрощує його розуміння:

Якщо при виклику конструктора аргумент для параметра не передається, автоматично підставляється значення за замовчуванням. Тобто new Patient() — це те саме, що new Patient("Невідомий", 0). Слід пам'ятати: параметри зі значеннями за замовчуванням завжди мають іти після параметрів без них.

Ініціалізатори об'єктів

Ініціалізатори — це синтаксична конструкція, яка дозволяє встановити значення доступних полів і властивостей об'єкта безпосередньо в момент його створення, без написання спеціального конструктора. Значення передаються у фігурних дужках після виклику конструктора:

При використанні ініціалізаторів слід враховувати два важливих моменти:

  • За допомогою ініціалізатора можна встановити значення лише тих полів і властивостей, які доступні за межами класу (тобто мають модифікатор public). Закриті (private) поля залишаються недоступними.
  • Ініціалізатор виконується після конструктора. Тому якщо і конструктор, і ініціалізатор встановлюють значення одного й того самого поля, перемагає значення з ініціалізатора.

Ініціалізатори особливо зручні, коли поле класу само по собі є об'єктом іншого класу. У такому випадку можна ініціалізувати вкладений об'єкт у тому ж виразі:

Зверніть увагу на синтаксис ініціалізації вкладеного об'єкта:

diagnosis = { title = "Грип" }

Тут не створюється новий об'єкт Diagnosis — натомість змінюється поле title вже існуючого об'єкта, який конструктор Patient() створив раніше. Саме тому такий запис можливий лише якщо конструктор уже ініціалізував поле diagnosis.

Деконструктори

Деконструктори (не плутати з деструкторами) дозволяють розкласти об'єкт на окремі складові — повернути кілька значень одночасно без використання кортежів або допоміжних структур. Деконструктор визначається як метод з іменем Deconstruct і параметрами out:

Значення з деконструктора передаються змінним за позицією: перше out-значення — першій змінній, друге — другій. Насправді деконструктор — це синтаксичний цукор над явним викликом методу:

string name;
int age;
patient.Deconstruct(out name, out age);

Обидва записи еквівалентні — компілятор перетворює деструктурування у звичайний виклик методу.

Буває, що не всі значення деконструктора потрібні. Щоб пропустити певне значення, замість змінної використовується символ відкидання _ (discard):

Оскільки перше значення — ім'я пацієнта, яке нас не цікавить, замість змінної стоїть _. Компілятор розуміє цей знак як «це значення мені не потрібне» і не виділяє для нього змінну.

Статичний конструктор

Окрім звичайних конструкторів, C# підтримує статичний конструктор — він виконується один раз автоматично перед першим зверненням до класу. Він не має модифікатора доступу, не приймає параметрів і не може бути викликаний явно. Його призначення — ініціалізація статичних полів або виконання одноразових налаштувань при «пробудженні» класу.

Зверніть увагу: статичний конструктор виконується до першого new Patient(...), але після рядка "Програма запускається..." — рівно в момент першого звернення до класу. Порядок гарантований середовищем виконання .NET.

Приватний конструктор

Конструктор може мати модифікатор private — тоді створити об'єкт класу ззовні буде неможливо. Це архітектурне рішення, яке використовується в патерні Singleton — коли система повинна мати рівно один екземпляр певного класу. У клінічній системі реєстр клініки логічно існує в єдиному примірнику:

Оскільки конструктор private, єдиний спосіб отримати об'єкт — через GetInstance(). Обидві змінні r1 і r2 вказують на один об'єкт — ReferenceEquals повертає true.

Первинні конструктори (C# 12)

Починаючи з C# 12, параметри конструктора можна оголошувати прямо в заголовку класу — це називається первинним конструктором (primary constructor). Він скорочує шаблонний код і робить оголошення класу компактнішим:

Параметри первинного конструктора (name, age) доступні у всьому тілі класу. Якщо потрібні додаткові конструктори — вони делегують виклик первинному через this(...):

Первинні конструктори особливо зручні для простих класів-моделей, де потрібно лише зберегти кілька значень і надати до них доступ.

Розроблено Tomka Yurii · © 2026 ·