1. Іменування кортежів: контекст для деконструкції
У попередній лекції ми вже познайомилися з іменованими елементами кортежів, що роблять код значно читабельнішим: до значень можна звертатися не через безликі Item1, Item2, а через змістовні імена (наприклад, person.Name). Можливість надавати імена елементам кортежу особливо важлива, коли їх використовують як значення, що повертаються методами, а також у параметрах і властивостях. Саме іменування є ключовою передумовою зручної деконструкції, яку ми розглянемо далі.
Нагадаємо: імена можна задавати під час ініціалізації кортежу через літерал, а також у сигнатурі поверненого значення методу, властивості чи поля.
Приклад з методом: Іменовані елементи кортежу у повертаному значенні методу
public static (int Age, string Name) GetPetInfo()
{
return (Age: 5, Name: "Барсик");
}
// Використання:
var info = GetPetInfo();
Console.WriteLine($"{info.Name} — {info.Age} років");
Приклад з полем/властивістю: Іменовані елементи кортежу у полі/властивості
public (int Width, int Height) ImageSize = (1024, 768);
Памʼятайте: якщо імена не задані явно, елементи усе одно будуть доступні через Item1, Item2 і далі. Це може зробити код менш читабельним, особливо під час подальшого використання кортежу. Тому рекомендуємо завжди надавати змістовні імена елементам, якщо їхній сенс не очевидний із контексту.
2. Деконструкція кортежів
Що таке деконструкція?
Деконструкція — це процес «розбирання» кортежу на окремі змінні, щоб потім зручно з ними працювати. Тобто з кортежу (Age: 5, Name: "Барсик") можна отримати дві змінні: age і name.
Аналогія: Уявіть, що кортеж — це коробка з підписаними комірками. Деконструкція — це коли ви одразу розкладаєте вміст коробки по своїх місцях на столі.
Синтаксис деконструкції
var pet = (Age: 5, Name: "Барсик");
var (age, name) = pet;
Console.WriteLine($"{name} — {age} років");
Тепер маємо дві змінні: age і name. Вони отримали значення з кортежу. Імена ліворуч (age, name) не обовʼязково мають збігатися з іменами у кортежі — це просто нові локальні змінні.
Деконструкція повертаного значення методу
Кортежі часто використовують, щоб повертати з методу кілька значень. Тут особливо зручно деконструювати:
public static (double min, double max) GetMinMax(int[] data)
{
int min = data.Min();
int max = data.Max();
return (min, max);
}
var numbers = new[] { 1, 2, 3, 4, 5 };
var (minValue, maxValue) = GetMinMax(numbers);
Console.WriteLine($"Мінімум: {minValue}, максимум: {maxValue}");
Зверніть увагу, що у деконструкції змінні можна називати так, як зручно в поточному контексті.
Деконструкція з var
var (a, b) = (10, 20); // int a = 10, b = 20
Деконструкція і discard _
Іноді не всі елементи кортежу потрібні. Їх можна проігнорувати за допомогою _ (discard). Дискард (_) у кортежі — це просто спосіб сказати: «я знаю, що тут ще є елемент, але він мені не потрібен, не створюй для нього змінну».
Отже, ви деконструюєте кортеж, але залишаєте в ньому «дірку» там, де вам не потрібне значення. Лаконічно, зручно і без компромісів!
var pet = (Age: 5, Name: "Барсик", IsHappy: true);
var (age, _, isHappy) = pet; // лише age та isHappy, імʼя проігнорували
Цікавий факт: discard активно використовують, щоб не засмічувати область видимості зайвими змінними.
Деконструкція в циклі foreach
У C# можна перебирати масив кортежів із деконструкцією прямо в самій інструкції foreach:
var pets = new (string Name, int Age)[]
{
("Барсик", 5),
("Муся", 3),
("Джонні", 7)
};
foreach (var (name, age) in pets)
{
Console.WriteLine($"{name} — {age} років");
}
3. Як працюють імена елементів і типізація
Поведінка іменованих елементів
Імена елементів кортежу — це «синтаксичний цукор», тобто спрощення для розробника. Під час компіляції імена перетворюються на внутрішні поля Item1, Item2 тощо, але IDE та компілятор зберігають імена, щоб ви могли їх використовувати.
Вплив на сумісність типів і перетворення
Два кортежі з однаковою кількістю елементів і типами, але різними іменами, вважаються однаковим типом для компілятора. Імена елементів не входять у визначення типу:
var t1 = (X: 42, Y: 13);
var t2 = (A: 42, B: 13);
t1 = t2; // OK
Console.WriteLine(t1.X); // 42
Водночас під час роботи з виразами та підказками IntelliSense використовуватимуться імена з лівої частини (тієї, куди присвоюємо), а не з правої.
Неявне й явне зазначення імен
Ми це вже розглядали, але про всяк випадок нагадаємо: можна створювати кортежі з частково іменованими елементами або взагалі без імен — тоді будуть просто Item1 тощо.
var point = (X: 10, 20); // X і Item2
Console.WriteLine(point.X); // 10
Console.WriteLine(point.Item2); // 20
Порада: завжди надавайте імена елементам, якщо кортеж містить понад один-два елементи або якщо сенс значення не зрозумілий із контексту.
4. Типові помилки та особливості під час роботи з іменами та деконструкцією
Помилка №1: «переїзд» імен під час присвоєння різних кортежів
Якщо ви спочатку оголосили кортеж з одними іменами, а потім присвоїли йому інший кортеж без імен (або з іншими іменами), в IntelliSense усе одно відображатимуться початкові імена. Наприклад:
var original = (X: 1, Y: 2);
var alias = original; // alias.X == 1, alias.Y == 2
original = (10, 20); // кортеж без імен
Console.WriteLine(alias.X); // усе ще працює, але alias усе ще зберігає старі імена
Помилка №2: дублювання імен елементів.
Не можна дати двом елементам однакове імʼя — компілятор видасть повідомлення «Duplicate tuple element name»:
var badTuple = (A: 1, A: 2); // Помилка CS8122: Duplicate tuple element name 'A'
Помилка №3: невірна деконструкція за кількістю елементів.
Під час деконструкції кількість змінних має точно збігатися з розміром кортежу. Будь-яка невідповідність призведе до помилки:
var pet = (Age: 5, Name: "Барсик");
var (age, name, mood) = pet; // Помилка CS8124: Tuple must contain exactly 3 elements
Помилка №4: неправильне використання discard _
Ігнорування елементів через _ працює для кожної позиції окремо і не «обʼєднує» пропуски в один. Наприклад, якщо ви намагаєтеся одночасно пропустити два елементи через один _, отримаєте помилку:
var data = (1, 2, 3);
var (_, x, _) = data; // Правильно: пропущені перший і третій елементи
var (_, _) = data; // Помилка CS8124: Tuple must contain exactly 2 elements
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ