Why is this an issue?
ValueTask<TResult>
was introduced in .NET Core 2.0 to optimize memory allocation when functions
return their results synchronously.
ValueTask
and ValueTask<TResult>
should never be used in the following ways 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 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:
- 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 more than one of these ways to consume the instance.
Noncompliant code example
ValueTask<int> vt = SomeValueTaskReturningMethodAsync();
int result = await vt;
int result2 = await vt; // Noncompliant, variable is awaited multiple times
int value = SomeValueTaskReturningMethodAsync().GetAwaiter().GetResult(); // Noncompliant, uses GetAwaiter().GetResult() when it's not known to be done
Compliant solution
int result = await SomeValueTaskReturningMethodAsync();
int result = await SomeValueTaskReturningMethodAsync().ConfigureAwait(false);
Task<int> t = SomeValueTaskReturningMethodAsync().AsTask();
Exceptions
This rule does not raise any issue when a ValueTask / ValueTask<TResult>
is awaited multiple time in a loop.
Resources