OOP Course
Сьогодні

Підрозділ 8.8

Анонімні типи

Розглядає анонімні типи, ініціалізатори об'єктів, проекційні ініціалізатори, read-only властивості та масиви анонімних об'єктів.

8.8. Анонімні типи

Іноді нам потрібен об'єкт лише для одного конкретного місця в коді: щоб тимчасово зберегти кілька пов'язаних значень, передати у функцію виведення або зібрати результат вибірки. Створювати повноцінний клас заради одноразового використання — надлишок. Для таких ситуацій у C# існують анонімні типи (anonymous types): об'єкти без явно оголошеного класу, де компілятор сам генерує внутрішній тип на льоту.

Анонімний тип визначається через var і ініціалізатор об'єкта без вказівки класу:

var patient = new { Name = "Іван Петренко", Age = 45 };
Console.WriteLine(patient.Name); // Іван Петренко
Console.WriteLine(patient.Age);  // 45

Тут немає жодного класу Patient — компілятор сам порозбирається з типом.

Що насправді генерує компілятор

Анонімний тип — це не магія. Компілятор генерує внутрішній клас з автоматичним іменем і підставляє його скрізь, де використовується var. Цей клас має конкретну структуру:

Анонімний тип: що генерує компілятор

sealed — анонімний тип не можна розширити спадкуванням.

readonly властивості — усі властивості анонімного типу доступні тільки для читання. Після ініціалізації змінити значення неможливо. Це свідомий дизайн: анонімні типи — immutable value containers.

Автоматичне ім'я — компілятор дає класу ім'я виду <>f__AnonymousType0. Кутові дужки <> — заборонений символ в ідентифікаторах C#. Це навмисно: програміст не може написати це ім'я у коді, що гарантує — ніхто не зможе зберегти посилання на тип у явно типізованій змінній.

Equals і GetHashCode — компілятор перевизначає обидва методи на основі структурного порівняння. Два анонімних об'єкти з однаковими іменами властивостей, типами та значеннями — рівні.

ToString — повертає рядок виду { Name = Іван Петренко, Age = 45 } — зручно для діагностики та виведення.

Основний синтаксис та ініціалізатори з проекцією

Крім явного присвоєння значень, анонімні типи підтримують ініціалізатори з проекцією (projection initializers): можна передати ідентифікатор або вираз, і ім'я змінної стане ім'ям властивості:

Рівність анонімних типів

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

Умова спільного визначення: однаковий набір властивостей, однаковий порядок, однакові типи. Якщо хоч щось відрізняється — компілятор створить окремий тип.

Масиви анонімних типів

Масив анонімних типів визначається через new[] — компілятор виведе тип елементів із першого ініціалізатора:

var patients = new[]
{
    new { Name = "Іван Петренко", Age = 45, Diagnosis = "Гіпертонія" },
    new { Name = "Марія Коваль",  Age = 32, Diagnosis = "Діабет"     },
    new { Name = "Олег Сидоренко",Age = 58, Diagnosis = "Аритмія"    },
};

foreach (var p in patients)
    Console.WriteLine($"{p.Name} ({p.Age} р.) — {p.Diagnosis}");

Усі елементи масиву повинні мати однаковий набір властивостей. Якщо хоч один відрізняється — помилка компіляції.

Область видимості та обмеження

Анонімний тип обмежений своєю областю видимості. Через var ми можемо зберегти об'єкт у локальній змінній, але не можна явно вказати тип анонімного об'єкта в сигнатурі методу, у полі класу чи в параметрі. Якщо потрібно передати анонімний об'єкт в інший метод, є лише два способи: через object (з втратою типізації) або через dynamic (з відкладеною перевіркою типів).

// НЕ можна:
// Patient GetAnonymous() => new { Name = "Іван", Age = 45 }; // помилка

// Можна через object (але доведеться кастити або використовувати рефлексію):
object obj = new { Name = "Іван", Age = 45 };

// Або через dynamic (повільно, без підтримки IDE):
dynamic dyn = new { Name = "Іван", Age = 45 };
Console.WriteLine(dyn.Name);

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

Readonly. Після створення об'єкта змінити його властивості не можна:

var patient = new { Name = "Іван", Age = 45 };
patient.Age = 46; // помилка компіляції — властивість readonly

Якщо потрібна змінювана структура — краще використовувати кортеж (8.9) або звичайний клас.

Зв'язок з LINQ

Анонімні типи є основним механізмом для проекцій у LINQ. Оператор select часто повертає не вихідний об'єкт, а лише потрібний набір полів — і саме тут анонімні типи незамінні:

// LINQ-вибірка лише потрібних полів (буде детально в розділі 13):
var summaries = patients.Select(p => new
{
    p.Name,
    p.Diagnosis,
    IsRisk = p.Age > 50
});

Кожен елемент summaries — це анонімний об'єкт з трьома полями. Клас для цього не потрібен — і не потрібен буде ніколи, бо ці об'єкти живуть лише в межах одного запиту.

Коли анонімний тип, а коли щось інше

Потреба Рекомендація
Тимчасова проекція в межах методу / LINQ-запиту Анонімний тип
Передача між методами з типізацією Кортеж (string Name, int Age) (розділ 8.9)
Багаторазово використовуваний іменований тип record (розділ 8.10) або клас
Потрібна мутабельність (зміна після створення) Клас зі звичайними властивостями
Потрібне успадкування Клас

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

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