Підрозділ 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 (неявно) → Person → Patient | Doctor. Базові типи знаходяться вгорі ієрархії, похідні — внизу.

Висхідні перетворення. Upcasting
Об'єкт похідного типу одночасно є об'єктом базового типу. Пацієнт (Patient) — це людина (Person), тому посилання на Patient можна зберегти у змінній типу Person. Таке перетворення від похідного до базового типу називається висхідним (upcasting) і відбувається неявно — без жодного додаткового синтаксису:
Змінні patient і person вказують на один і той самий об'єкт у пам'яті. Але через змінну person доступна лише та частина функціоналу, яку визначає тип Person — властивість Diagnosis буде недоступна.
Висхідне перетворення відбувається і під час присвоєння до типу object, оскільки він є базовим для всіх:
Зверніть увагу: метод GetType() завжди повертає реальний тип об'єкта, незалежно від типу змінної, що його зберігає.

Низхідні перетворення. 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 є найчистішим і найбезпечнішим способом роботи з поліморфними колекціями, оскільки компілятор перевіряє повноту гілок і видає попередження, якщо якийсь тип не оброблено.