Ответ
Асинхронный метод (async) в C# может возвращать только следующие типы, которые представляют собой выполняемую или уже завершенную асинхронную операцию:
-
Task- Назначение: Для асинхронных операций, которые не возвращают значение (аналог
voidв синхронном мире). - Пример: Метод, который что-то сохраняет в базу данных или отправляет уведомление.
public async Task SaveDataAsync(Data data) { await _repository.SaveAsync(data); _logger.LogInformation("Data saved."); }
- Назначение: Для асинхронных операций, которые не возвращают значение (аналог
-
Task<TResult>- Назначение: Для асинхронных операций, возвращающих результат типа
TResult. - Пример: Метод, запрашивающий данные из API или БД.
public async Task<Product> GetProductByIdAsync(int id) { return await _dbContext.Products.FindAsync(id); }
- Назначение: Для асинхронных операций, возвращающих результат типа
-
ValueTask/ValueTask<TResult>- Назначение: Легковесные структуры, которые следует использовать для оптимизации, когда результат операции часто доступен синхронно (например, кэширован в памяти). Они позволяют избежать лишних аллокаций в куче, в отличие от
Task. -
Пример: Метод, который часто возвращает данные из кэша.
public async ValueTask<string> GetCachedDataAsync(string key) { if (_cache.TryGetValue(key, out string cachedData)) return cachedData; // Синхронный возврат, аллокации Task нет! // Асинхронная загрузка, если нет в кэше var data = await _service.FetchDataAsync(key); _cache.Set(key, data); return data; }
- Назначение: Легковесные структуры, которые следует использовать для оптимизации, когда результат операции часто доступен синхронно (например, кэширован в памяти). Они позволяют избежать лишних аллокаций в куче, в отличие от
-
void- Назначение: Крайне не рекомендуется, кроме одного специфического случая — обработчиков событий (например,
button.Click += async (sender, e) => { ... }). - Почему плохо: Вызывающий код не может дождаться завершения такой операции (
awaitневозможен) и не сможет перехватить исключения, выброшенные внутри метода. Это приводит к неожиданным сбоям.
- Назначение: Крайне не рекомендуется, кроме одного специфического случая — обработчиков событий (например,
Ключевое правило: В 99% случаев используйте Task или Task<T>. Переходите на ValueTask только после профилирования и доказательства, что это дает выгоду в конкретном горячем пути.