ValueTask<TResult>
provides a value type that wraps a Task<TResult> and the corresponding TResult
.
It was introduced in .NET Core 2.0 to optimize
memory allocation when functions return their results synchronously.
Using ValueTask
and ValueTask<TResult>
in the following ways is discouraged as it could result in a race
condition:
- Calling
await
multiple times on a ValueTask
/ValueTask<TResult>
. The wrapped object may have been
reused by another operation. This differs from Task
/Task<TResult>
, on which you can await multiple times and always
get the same result.
- Calling
await
concurrently on a ValueTask
/ValueTask<TResult>
. The underlying object is not thread
safe. What’s more, it has the same effect as awaiting multiple times a ValueTask
/ValueTask<TResult>
. This again
differs from Task
/Task<TResult>
, which support concurrent await
.
- Using
.Result
or .GetAwaiter().GetResult()
without checking if the operation is completed.
IValueTaskSource
/IValueTaskSource<TResult>
implementations are not required to block until the operation completes.
On the other hand, Task
/Task<TResult>
blocks the call until the task completes.
It is recommended to use ValueTask
/ValueTask<TResult>
either by calling await
on the function
returning it, optionally calling ConfigureAwait(false)
on it, or by calling .AsTask()
on it.
This rule raises an issue when the following operations are performed on a ValueTask
/ValueTask<TResult>
instance
unless it happens in a loop:
- Awaiting the instance multiple times.
- Calling
AsTask
multiple times.
- Using
.Result
or .GetAwaiter().GetResult()
multiple times
- Using
.Result
or .GetAwaiter().GetResult()
when the operation has not yet completed
- Using of these ways to consume the instance multiple times.