OOP Course
Сьогодні

Підрозділ 4.10

Наслідування узагальнених типів

Показує різні варіанти наслідування узагальнених класів і перенесення обмежень у похідні типи.

4.10. Наслідування узагальнених типів

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

Як базовий клас для всіх прикладів використаємо MedicalRecord<T>:

abstract class MedicalRecord<T>
{
    public T Id { get; }
    public string Description { get; set; }

    public MedicalRecord(T id, string description)
    {
        Id          = id;
        Description = description;
    }

    public override string ToString() => $"[{Id}] {Description}";
}

4 варіанти успадкування узагальнених класів

Варіант 1: передача параметра далі — Child : Base

Похідний клас залишається узагальненим і передає свій параметр T у базовий клас. Це найбільш гнучкий варіант — конкретний тип визначається лише при створенні екземпляра:

FlexibleRecord<int> і FlexibleRecord<string> — це два різних класи, кожен зі своїм типом Id. Похідний клас просто «пробрасує» параметр у базовий.

Варіант 2: фіксація типу — Child : Base

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

InpatientRecord — звичайний клас без власних параметрів типу. Він завжди має Id типу string, і його можна зберігати у змінній MedicalRecord<string>.

Варіант 3: власний параметр при фіксованому базовому — Child : Base

Похідний клас є узагальненим з власним параметром T, але у базового клас тип зафіксований. Це дозволяє додавати нові generic-поля незалежно від базового:

TaggedRecord<T> успадковує MedicalRecord<int> — тому Id завжди int. Але сам клас залишається параметричним за T, що використовується для поля Tag.

Варіант 4: розширення базового параметра — Child : Base

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

AnnotatedRecord<T, TNote> повністю гнучкий: T визначає тип Id (як у базовому), а TNote — тип додаткової анотації. Обидва параметри незалежні.

Успадкування обмежень

Якщо базовий клас встановлює обмеження на параметр типу, похідний клас зобов'язаний підтримати або посилити це обмеження для того самого параметра:

Правило просте: якщо базовий клас має where T : class, то і похідний повинен вказати where T : class або конкретніше обмеження (наприклад, where T : Patient). Це гарантує, що компілятор зможе перевіряти обмеження на всіх рівнях ієрархії.

Підсумок: коли що використовувати

Варіант Синтаксис Коли застосовувати
Передача T Child<T> : Base<T> Похідний залишається гнучким
Фіксація типу Child : Base<string> Тип відомий — клас стає конкретним
Свій T, фіксований базовий Child<T> : Base<int> Додаємо гнучкість у нових полях
Розширення параметрів Child<T, K> : Base<T> Максимальна гнучкість
Розроблено Tomka Yurii · © 2026 ·