1840.md
February 3, 2022 ยท View on GitHub
The consumption of the newer and performance related ValueTask and ValueTask<T> types is more restrictive than consuming Task or Task<T>. Starting with .NET Core 2.1 the ValueTask<T> is not only able to wrap the result T or a Task<T>, with this version it is also possible to wrap a IValueTaskSource / IValueTaskSource<T> which gives the developer extra support for reuse and pooling. This enhanced support might lead to unwanted side-effects, as the ValueTask-returning developer might reuse the underlying object after it got awaited. The safest way to consume a ValueTask / ValueTask<T> is to directly await it once, or call .AsTask() to get a Task / Task<T> to overcome these limitations.
// OK / GOOD
int bytesRead = await stream.ReadAsync(buffer, cancellationToken);
// OK / GOOD
int bytesRead = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
// OK / GOOD - Get task if you want to overcome the limitations exposed by ValueTask / ValueTask<T>
Task<int> task = stream.ReadAsync(buffer, cancellationToken).AsTask();
Other usage patterns might still work (like saving the ValueTask / ValueTask<T> into a variable and awaiting later), but may lead to misuse eventually. Not awaiting a ValueTask / ValueTask<T> may also cause unwanted side-effects. Read more about ValueTask / ValueTask<T> and the correct usage here.