OOP Course
Сьогодні

Підрозділ 17.2

Типи повернення async-методів

17.2. Типи повернення async методів Асинхронні методи в C можуть повертати чотири типи: void , Task , Task<T і ValueTask<T . Вибір між ними — не питання смаку: кожен тип має своє призначення, обмеження та вплив

17.2. Типи повернення async-методів

Асинхронні методи в C# можуть повертати чотири типи: void, Task, Task<T> і ValueTask<T>. Вибір між ними — не питання смаку: кожен тип має своє призначення, обмеження та вплив на продуктивність. Правильний вибір типу повернення — ознака грамотного асинхронного коду.

Типи повернення async-методів

async void — тільки для обробників подій

async void — найнебезпечніший тип повернення. Метод нічого не повертає, тому викликаючий код не може ні отримати результат, ні дочекатися завершення, ні перехопити виняток через try/catch.

Єдиний законний сценарій використання async void — обробники подій, де підпис методу встановлений зовнішнім інтерфейсом і не може бути змінений:

Ключове правило: ніколи не використовуйте async void поза обробниками подій. async void-метод не можна очікувати, не можна обробити його виняток ззовні, не можна перевірити його стан. Це «запустив і забув» у найгіршому сенсі.

async Task — операція без результату

async Task — стандартний тип повернення для асинхронних методів, що не повертають значення. На відміну від async void, Task дозволяє:

  • очікувати завершення через await
  • перехоплювати винятки через try/catch навколо await
  • передавати завдання у Task.WhenAll та інші комбінатори

async Task завжди кращий за async void там, де підпис методу під вашим контролем. Поверніть Task — і ваш код стає тестованим, перехоплюваним і сумісним з усіма async-комбінаторами.

async Task — операція з результатом

Task<T> — тип повернення для асинхронних методів, що обчислюють або отримують значення. Оператор await «розгортає» Task і повертає значення типу T. Це найпоширеніший тип для реальних асинхронних операцій:

Зверніть на return у async Task<T>: ви повертаєте значення типу T, а не Task<T>. Компілятор автоматично «загортає» його у Task<T>. Це симетрично до того, як await «розгортає» Task<T> назад до T.

ValueTask — для hot path без алокації

ValueTask<T> — оптимізований тип для сценаріїв, де метод часто завершується синхронно (без реального очікування). Різниця з Task<T>: якщо результат вже відомий і очікування не потрібне, ValueTask<T> не виділяє пам'яті у heap, оскільки є структурою (struct), а не класом.

ValueTask<T> слід використовувати лише коли є вимірювані докази, що алокація Task<T> є вузьким місцем (hot path, мільйони викликів). В усіх інших випадках — Task<T> є правильним вибором: він простіший, безпечніший і добре оптимізований у .NET.

Важливе обмеження: ValueTask<T> можна await-ати лише один раз. Якщо вам потрібно зберегти завдання у змінну і очікувати кілька разів — використовуйте Task<T>.

Task.FromResult і Task.CompletedTask — синхронні обгортки

Іноді потрібно повернути Task<T> або Task з методу, де фактична робота виконується синхронно. Найпоширенісний сценарій — реалізація інтерфейсу або тест-дублер:

Task.FromResult<T>(value) створює Task, що вже завершений зі значенням — без жодних алокацій стейт-машини. Task.CompletedTask — синглтон-Task для Task-методів без значення, що не вимагає реальної асинхронності. Обидва широко використовуються при реалізації інтерфейсів, заглушок і тест-дублерів.

Зведена таблиця типів повернення

Тип Коли використовувати Awaitable Перехоплення помилок Алокація
async void Тільки EventHandler Ні Ні (глобально) Немає
async Task Операція без результату Так Так Task
async Task<T> Операція з результатом Так Так Task
async ValueTask<T> Hot path з частим sync-шляхом Так (1 раз) Так Немає (sync)

Правило вибору: за замовчуванням — Task або Task<T>. async void — лише для EventHandler. ValueTask<T> — тільки при підтвердженому профілюванням вузькому місці.

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