TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #1248 1.46K
День 1020. #ЗаметкиНаПолях #AsyncTips
Когда Использовать ValueTask. Начало
ValueTask<T> используется как возвращаемый тип в ситуациях, в которых чаще всего может быть возвращён синхронный результат, а асинхронное поведение встречается реже. Обычно следует использовать Task<T>, а не ValueTask<T>. Рассматривать использование ValueTask<T> в качестве возвращаемого типа следует только после профилирования, которое показывает, что это приведёт к повышению быстродействия.

Простейший способ реализации метода, возвращающего ValueTask<T>, основан на использовании async и await, как и обычный async-метод. Нередко метод, возвращающий ValueTask<T>, способен немедленно вернуть значение. В таких случаях можно применить оптимизацию для этого сценария с использованием конструктора ValueTask<T>, и передавать управление медленному асинхронному методу только при необходимости:
public ValueTask<int> MethodAsync()
{
if (CanBehaveSynchronously)
return new ValueTask<int>(13);

return new ValueTask<int>(SlowMethodAsync());
}
private Task<int> SlowMethodAsync();

Возможны ситуации, в которых требуется реализовать метод, возвращающий ValueTask<T>. Например, при использовании интерфейса IAsyncDisposable, метод DisposeAsync которого возвращает ValueTask. В C# 8.0 и .NET Core 3.0 появилось асинхронное освобождение ресурсов. В BCL появился новый интерфейс IAsyncDisposable, который является асинхронным аналогом IDisposable. Также была введена команда await using — асинхронный аналог using. Таким образом, типы, которые собирались выполнить асинхронную работу при освобождении, получили такую возможность. Возвращаемым типом DisposeAsync является ValueTask, а не Task.

В следующем примере показана реализация IAsyncDisposable, которая выполняет свою логику асинхронного освобождения однократно. При следующих вызовах метод DisposeAsync завершается успешно и синхронно:
class MyClass : IAsyncDisposable
{
private Func<Task> _disposeLogic;

public ValueTask DisposeAsync()
{
if (_disposeLogic == null)
return default;

// Этот простой пример непотокобезопасный;
// если сразу несколько потоков вызовут DisposeAsync,
// логика может быть выполнена более одного раза.
Func<Task> logic = _disposeLogic;
_disposeLogic = null;

return new ValueTask(logic());
}
}

Использование:
await using (var myClass = new MyClass())
{
...
} // Здесь вызывается DisposeAsync (с ожиданием)

Большинство методов должно возвращать Task<T>, поскольку при потреблении Task<T> возникает меньше скрытых ловушек, чем при потреблении ValueTask<T>. Чаще всего при реализации интерфейсов, использующих ValueTask или ValueTask<T>, можно просто применять async и await.

Окончание следует…

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Главы 2, 11.
More from @netdeveloperdiary
  1. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  2. Oct 3, 2026Post #3360
  3. Oct 3, 2026День 2803. #Оффтоп Чем Заняться, Пока Работают Агенты? У VS Code Есть Ответ Сейчас большую…
  4. Oct 2, 2026День 2802. #Карьера #Юмор Секреты Программирования, Известные Только Легендам Ещё один пос…
  5. Oct 1, 2026Post #3357
  6. Sep 30, 2026Post #3356
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →