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
ValueTaskis a performance optimization over aTasks, but this performance comes with a cost: You cannot use aValueTaskas freely as aTask. The documentation mentions these restrictions:These restrictions apply only for
ValueTasks that are backed by aIValueTaskSource. AValueTaskcan also be backed by aTask, or by aTResultvalue (for aValueTask<TResult>). If you know with 100% certainty that aValueTaskis not backed by anIValueTaskSource, you can use it as freely as aTask. For example you canawaitis multiple times, or you can wait it synchronously to complete with.GetAwaiter().GetResult().In general you don't know how a
ValueTaskis implemented internally, and even if you know (by studying the source code) you may not want to rely on an implementation detail. With theValueTask.Preservemethod you can create a newValueTaskthat represents the originalValueTask, and can be used without restrictions because it's not baked by anIValueTaskSource. This method affects the originalValueTaskonly if it's backed by anIValueTaskSource, otherwise it's a no-op (it just returns the original task unaltered).After calling
Preserve, the originalValueTaskhas been consumed. It should no longer be awaited, orPreserved again, or converted toTaskwith theAsTaskmethod. Doing any of those actions is likely to result in anInvalidOperationException. But now you have thepreservedrepresentation of it, which can be used with the same freedom as aTask.My answer included initially a practical example of using the
Preservemethod, which proved to be incorrect. This example can be found in the 4th revision of this answer.