OOP Course
Сьогодні

Підрозділ 4.3

Віртуальні методи та властивості

Пояснює virtual і override, перевизначення методів та властивостей, використання base і заборону перевизначення через sealed.

4.3. Віртуальні методи та властивості

При успадкуванні нерідко виникає необхідність змінити в похідному класі поведінку методу, успадкованого від базового. Стандартний метод неможливо перевизначити у нащадку — для цього базовий клас має явно дозволити перевизначення за допомогою ключового слова virtual. Такі методи та властивості називаються віртуальними.

У похідному класі метод, що замінює реалізацію базового, позначається ключовим словом override. Сигнатура перевизначеного методу — ім'я та параметри — повинна точно збігатися з сигнатурою базового віртуального методу.

Оголошення virtual та override

Розглянемо клінічну ієрархію: базовий клас Person з віртуальним методом Print(), та похідні класи Patient і Doctor, кожен з яких перевизначає цей метод по-своєму:

Якщо похідний клас не перевизначає віртуальний метод — використовується реалізація базового класу. Тобто virtual лише дозволяє перевизначення, але не зобов'язує до нього.

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

  • Модифікатор доступу перевизначеного методу повинен збігатися з модифікатором базового (public virtualpublic override).
  • Неможливо оголосити virtual або перевизначити static-метод.
  • Неможливо перевизначити метод без virtual у базовому класі (для цього є приховування, яке розглядається в розділі 4.4).

Поліморфізм: виклик за реальним типом об'єкта

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

Коли через змінну типу Person викликається метод Print(), середовище виконання визначає реальний тип об'єкта під час виконання програми і викликає відповідну реалізацію — Patient.Print() для пацієнта, Doctor.Print() для лікаря:

Масив оголошено як Person[], але кожен елемент зберігає об'єкт свого реального типу. Під час виклику p.Print() C# не дивиться на тип змінної — він шукає реалізацію методу у фактичному типі об'єкта. Цей механізм називається пізнім зв'язуванням (late binding або dynamic dispatch).

Virtual dispatch — виклик методу за реальним типом об'єкта

Поліморфізм є фундаментальним принципом ООП: він дозволяє писати код, який працює з абстракцією (Person), не знаючи конкретних типів — і при цьому кожен об'єкт поводиться відповідно до своєї природи.

Звернення до базового методу через base

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

Виклик base.Print() запускає саме реалізацію класу Person, навіть якщо викликається через об'єкт Doctor. Це типовий патерн для поступового збагачення поведінки в ланцюжку успадкування.

Ланцюжок перевизначень

Перевизначення може тривати через кілька рівнів ієрархії. Кожен похідний клас може перевизначити метод далі — або знову використати base:

Ланцюжок перевизначень virtual → override

При виклику p.Print() через змінну типу Person, де реальний тип Inpatient, спочатку виконується Inpatient.Print(), яка через base.Print() викликає Patient.Print(), яка у свою чергу через base.Print() викликає Person.Print().

Перевизначення властивостей

Так само як і методи, властивості можна оголошувати virtual і перевизначати в нащадках за допомогою override. Це корисно, коли похідний клас має накладати додаткові обмеження або змінювати логіку доступу:

У класі Person властивість Age перевіряє лише загальний діапазон (1..119). У класі Doctor перевизначена властивість додає вимогу мінімального віку 25 років. При встановленні значення менше допустимого воно просто ігнорується.

Заборона подальшого перевизначення: sealed

Якщо перевизначений метод у похідному класі не повинен перевизначатися у його нащадках, його оголошують з модифікатором sealed. Цей модифікатор завжди використовується разом з override:

sealed override означає: «я перевизначаю метод базового класу, але забороняю будь-якому нащадку перевизначати його далі». Це корисно, коли реалізація є критичною і не повинна бути змінена у підкласах — наприклад, метод, що реалізує специфічну логіку ліцензування або безпеки.

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