OOP Course
Сьогодні

Підрозділ 4.8

Узагальнення

Пояснює generics: універсальні параметри, типобезпеку, boxing/unboxing, кілька параметрів, статичні поля і узагальнені методи.

4.8. Узагальнення

Крім звичайних типів, фреймворк .NET підтримує узагальнені типи (generics) — класи, структури, інтерфейси та методи, які параметризуються типом. Generics є одним із найважливіших механізмів C# і широко використовуються у стандартній бібліотеці: List<T>, Dictionary<TKey, TValue>, Queue<T> та багато інших.

Проблема без узагальнень

Щоб зрозуміти навіщо потрібні generics, розглянемо задачу. У клінічній системі медична картка пацієнта може мати ідентифікатор різного типу: в одній системі це int, в іншій — string типу "P-001", у третій — Guid. Як написати один клас, що підтримує всі варіанти?

Перше рішення — використати object, оскільки це базовий тип для всіх:

Два суттєві недоліки цього підходу:

  1. Boxing/unboxing — при передачі int у object відбувається упаковка (boxing): значення зі стека копіюється у купу, де для нього виділяється об'єкт-обгортка. Зворотна операція (розпакування) — теж копіювання. У гарячих ділянках коду це помітно знижує продуктивність.

  2. Відсутність типобезпеки — компілятор не знає реального типу Id, тому не може попередити про неправильне приведення. Помилка виникне лише під час виконання як InvalidCastException.

Узагальнений клас

Generics вирішують обидві проблеми. Замість object використовується параметр типу T — своєрідний «слот», який заповнюється конкретним типом при використанні класу:

Узагальнений клас: один шаблон — різні типи

Кутові дужки <T> в оголошенні class MedicalRecord<T> вказують, що клас є узагальненим. Буква T — умовна назва параметра типу; замість неї може бути будь-який ідентифікатор. При створенні об'єкта конкретний тип вказується у кутових дужках: new MedicalRecord<int>(...). Компілятор замінює T на int і перевіряє всі операції з Id відповідно до цього типу — помилки типів виловлюються на етапі компіляції, а не виконання.

Конвенції іменування параметрів типу

У C# прийнято такі угоди щодо назв параметрів типу:

  • T — загальний параметр типу (скорочення від Type)
  • TKey, TValue — для пар ключ/значення (як у Dictionary<TKey, TValue>)
  • TResult — для типу результату (як у Func<T, TResult>)
  • TEntity, TModel — описові назви для конкретного контексту

Кілька параметрів типу

Клас може мати кілька параметрів типу одночасно. Наприклад, клас призначення Assignment пов'язує пацієнта та лікаря, причому обидва можуть мати свої типи ідентифікаторів:

Кожен параметр типу замінюється незалежно: TPatientIdint, TDoctorIdstring.

Статичні поля узагальнених класів

Важливий нюанс: при типізації узагальненого класу різними типами для кожної комбінації типів створюється окремий набір статичних членів. Тобто MedicalRecord<int> і MedicalRecord<string> — це фактично два різних класи з власними статичними полями:

Узагальнений клас як тип поля

Параметр типу може сам бути узагальненим класом. Наприклад, клас Clinic зберігає головного лікаря як MedicalRecord<T>:

Узагальнені методи

Generics застосовуються не лише до класів, але й до окремих методів. Узагальнений метод оголошує власний параметр типу і може бути визначений у будь-якому класі — в тому числі у звичайному (не generic):

Зверніть увагу на виклик Swap(ref s1, ref s2) без явного вказання типу — компілятор сам визначає T = string з типів переданих аргументів. Це називається виведення типу (type inference) і робить код лаконічнішим.

Generics у стандартній бібліотеці

Generics є основою колекцій .NET. Завдяки ним колекції зберігають елементи без boxing і з повною типобезпекою:

List<string> зберігає рядки безпосередньо, без упаковки в object — на відміну від старого ArrayList. Dictionary<int, string> типізує і ключ, і значення, тому жодних явних приведень не потрібно. Детально колекції розглядаються у наступних розділах курсу.

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