JavaRush /Курси /C# SELF /Повернення кількох значень з функції: out-параметри та ко...

Повернення кількох значень з функції: out-параметри та кортежі

C# SELF
Рівень 11 , Лекція 3
Відкрита

1. Вступ

Уявімо ситуацію: є функція, яка за вказаним іменем користувача має повернути одразу і вік, і дату реєстрації, і прапорець активності. У C та ранніх версіях C# вважалося нормальним мати лише одне значення, що повертається. А що робити, якщо значень кілька? Це питання породило кілька підходів, кожен із яких має свої плюси й мінуси.
У попередніх лекціях ви вже знайомилися з кортежами. Зараз розглянемо out-параметри й порівняємо ці два підходи.

Найпопулярніші підходи:

  • Використовувати out-параметри.
  • Повернути об’єкт із потрібними полями (анонімний тип або користувацький клас).
  • Повернути кортеж (ValueTuple).

У цій лекції ми детально розглянемо два з цих підходів — out-параметри та кортежі — і порівняємо їх «віч-на-віч», щоб зрозуміти переваги кожного для повернення кількох значень із функції.

2. out-параметри: привіт із минулого

Коли бачите метод на кшталт:


void GetUserInfo(string userName, out int age, out DateTime registrationDate, out bool isActive)
{
    // тут обчислення
    age = 42;
    registrationDate = new DateTime(2010, 1, 1);
    isActive = true;
}

мимоволі замислюєшся: це метод чи маленька автомийка? Стільки всього повертається назовні — і все повз основне значення, що повертається!

Як це працює

Код, що викликає метод, має заздалегідь оголосити змінні, які метод заповнить:

int age;
DateTime reg;
bool isActive;

GetUserInfo("Bob", out age, out reg, out isActive);

// Тепер усі змінні заповнені
Console.WriteLine($"{age}, {reg}, {isActive}");

Мінуси out-підходу

  • Складніше читати: Сигнатура методу стає громіздкою, сенс повернених даних не завжди зрозумілий із самих імен параметрів.
  • Незручно у ланцюжках викликів: Такий метод важко вставити в ланцюжок (наприклад, одразу передати результат іншому методу).
  • Змінює передані змінні: Метод зобовʼязаний модифікувати наявні змінні; якщо забути про out, буде помилка компіляції.
  • Зайві оголошення: Компілятор змушує вас оголошувати зовнішні змінні, навіть якщо ви хочете використати результат лише раз.
  • Обмежена читабельність у великих методах: Якщо out-параметрів багато, легко заплутатися, що за чим і для чого потрібно.

Саме тут кортежі розкривають свої переваги.

3. Кортежі — еволюція підходу

Порівняємо на прикладі

За допомогою кортежу:


public (int Age, DateTime RegistrationDate, bool IsActive) GetUserInfo(string userName)
{
    // емуляція пошуку в базі
    return (42, new DateTime(2010, 1, 1), true);
}

Використання:

// Отримуємо одразу всі значення й даємо їм імена змінних
var (age, regDate, isActive) = GetUserInfo("Bob");
Console.WriteLine($"{age}, {regDate}, {isActive}");

Без зайвих оголошень, без out — усе максимально читабельно!

Таблиця порівнянь: out vs tuple

Критерій out‑параметри Кортежі (ValueTuple)
Сигнатура довга: out‑параметри в списку компактна, усе повертається однією «посилкою»
Використання треба оголосити змінні, потім викликати метод можна одразу деконструювати
Читабельність часто губиться, якщо out‑параметрів кілька імена елементів зрозумілі одразу
Легко комбінувати? ні так, можна вкладати й передавати далі

Кортежі vs out — що відбувається «під капотом»

З out-параметрами метод насправді працює з памʼяттю поза своєю «територією»: якщо трохи спростити, він змінює змінні, які були створені десь іще. Це вимагає обережності, бо можна ненароком «засмітити» стан змінних (дякуємо компілятору за обовʼязкове присвоєння!).

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

Читання й супровід коду

Погодьтеся: коли за тиждень ви читаєте чужий код, хочеться бачити:

(var age, var city, var isActive) = GetUserInfo("Anna");

а не:

int age;
string city;
bool isActive;
GetUserInfo("Anna", out age, out city, out isActive);

Кортежі роблять сигнатури методів менш «шумними», а результат їх використання — інтуїтивно зрозумілим.

4. Ситуації, де кортежі особливо зручні

1. Коли потрібно повернути результат і повідомлення про помилку

public (bool Success, string ErrorMessage) TryProcess(string data)
{
    if (string.IsNullOrEmpty(data))
        return (false, "Немає даних");

    // обробка даних...
    return (true, "");
}

var (ok, error) = TryProcess(input);
if (!ok)
    Console.WriteLine($"Помилка: {error}");

2. Для функцій, що повертають багато результатів пошуку

public (User? FoundUser, int Index) FindUserByName(User[] users, string name)
{
    for (int i = 0; i < users.Length; i++)
    {
        if (users[i].Name == name)
            return (users[i], i);
    }
    return (null, -1);
}

3. Для «пар» значень: розрахунок мінімуму й максимуму

public (int min, int max) FindMinMax(int[] numbers)
{
    int min = numbers[0], max = numbers[0];
    foreach (var n in numbers)
    {
        if (n < min) min = n;
        if (n > max) max = n;
    }
    return (min, max);
}

var (minValue, maxValue) = FindMinMax(arr);
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ