ValueTask
and ValueTask<TResult>
have a Preserve()
method which is summarized as "Gets a ValueTask that may be used at any point in the future."
What does that mean and when should we use it? Does it imply that a 'normal' ValueTask
can't be used "at any point in the future"? If so, why?
The
ValueTask
is a performance optimization over aTask
s, but this performance comes with a cost: You cannot use aValueTask
as freely as aTask
. The documentation mentions these restrictions:These restrictions apply only for
ValueTask
s that are backed by aIValueTaskSource
. AValueTask
can also be backed by aTask
, or by aTResult
value (for aValueTask<TResult>
). If you know with 100% certainty that aValueTask
is not backed by anIValueTaskSource
, you can use it as freely as aTask
. For example you canawait
is multiple times, or you can wait it synchronously to complete with.GetAwaiter().GetResult()
.In general you don't know how a
ValueTask
is implemented internally, and even if you know (by studying the source code) you may not want to rely on an implementation detail. With theValueTask.Preserve
method you can create a newValueTask
that represents the originalValueTask
, and can be used without restrictions because it's not baked by anIValueTaskSource
. This method affects the originalValueTask
only if it's backed by anIValueTaskSource
, otherwise it's a no-op (it just returns the original task unaltered).After calling
Preserve
, the originalValueTask
has been consumed. It should no longer be awaited, orPreserve
d again, or converted toTask
with theAsTask
method. Doing any of those actions is likely to result in anInvalidOperationException
. But now you have thepreserved
representation of it, which can be used with the same freedom as aTask
.My answer included initially a practical example of using the
Preserve
method, which proved to be incorrect. This example can be found in the 4th revision of this answer.