OOP Course
Сьогодні

Підрозділ 4.2

Перетворення типів

Розглядає висхідні та спадні перетворення між класами, ризики downcasting і безпечні перевірки через as та is.

4.2. Перетворення типів

У попередньому розділі ми говорили про успадкування та відношення is-a між класами. З цим відношенням безпосередньо пов'язана можливість перетворення типів у ієрархії класів: об'єкт похідного класу в будь-який момент може бути представлений як об'єкт базового класу, і навпаки — за певних умов. Розуміння правил таких перетворень є обов'язковим для написання гнучкого коду, що працює з ієрархіями об'єктів.

Розглянемо клінічну ієрархію класів:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age  = age;
    }

    public void Print()
    {
        Console.WriteLine($"Особа: {Name}, {Age} р.");
    }
}

class Patient : Person
{
    public string Diagnosis { get; set; }

    public Patient(string name, int age, string diagnosis) : base(name, age)
    {
        Diagnosis = diagnosis;
    }
}

class Doctor : Person
{
    public string Specialization { get; set; }

    public Doctor(string name, int age, string specialization) : base(name, age)
    {
        Specialization = specialization;
    }
}

Ланцюг успадкування: Object (неявно) → PersonPatient | Doctor. Базові типи знаходяться вгорі ієрархії, похідні — внизу.

Ієрархія типів у клінічній системі

Висхідні перетворення. Upcasting

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

Змінні patient і person вказують на один і той самий об'єкт у пам'яті. Але через змінну person доступна лише та частина функціоналу, яку визначає тип Person — властивість Diagnosis буде недоступна.

Висхідне перетворення відбувається і під час присвоєння до типу object, оскільки він є базовим для всіх:

Зверніть увагу: метод GetType() завжди повертає реальний тип об'єкта, незалежно від типу змінної, що його зберігає.

Upcasting та Downcasting у ієрархії класів

Низхідні перетворення. Downcasting

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

Якщо ж реальний тип об'єкта не відповідає типу, до якого відбувається приведення, виникає виняток InvalidCastException під час виконання програми:

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

Оператор as

Оператор as намагається виконати перетворення і у разі невдачі повертає null замість викидання винятку. Це безпечна альтернатива явному приведенню:

Тип результату після as завжди nullable (Patient?, Doctor?) — тобто може містити або об'єкт, або null. Перевірка на null перед використанням є обов'язковою.

Оператор is

Оператор is перевіряє, чи є об'єкт представником певного типу, і повертає true або false. Починаючи з C# 7, він підтримує перевірку з одночасним перетворенням (pattern matching): якщо перевірка успішна, об'єкт автоматично приводиться до потрібного типу і зберігається у нову змінну:

Вираз person is Patient patient робить одразу дві речі: перевіряє тип і, якщо він відповідає, зберігає вже приведений об'єкт у змінну patient. Це лаконічніше і безпечніше, ніж окрема перевірка + явне приведення.

Pattern matching з switch

Починаючи з C# 8, оператор switch підтримує pattern matching по типах. Це зручно, коли потрібно обробити кілька різних типів з однієї ієрархії:

Важливий нюанс: гілки switch перевіряються зверху донизу, тому більш специфічні типи (Patient, Doctor) мають стояти перед більш загальним (Person). Якщо поставити Person per першим, він захопить усі об'єкти і до гілок Patient та Doctor справа не дійде.

Switch з pattern matching є найчистішим і найбезпечнішим способом роботи з поліморфними колекціями, оскільки компілятор перевіряє повноту гілок і видає попередження, якщо якийсь тип не оброблено.

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