As of C# 7.0 async methods can return ValueTask
From the API docs (emphasis added):
Methods may return an instance of this value type when it's likely that the result of their operations will be available synchronously and when the method is expected to be invoked so frequently that the cost of allocating a new
Taskfor each call will be prohibitive.There are tradeoffs to using a
ValueTaskinstead of aTask. For example, while aValueTaskcan help avoid an allocation in the case where the successful result is available synchronously, it also contains two fields whereas aTaskas a reference type is a single field. This means that a method call ends up returning two fields worth of data instead of one, which is more data to copy. It also means that if a method that returns one of these is awaited within anasyncmethod, the state machine for thatasyncmethod will be larger due to needing to store the struct that's two fields instead of a single reference.Further, for uses other than consuming the result of an asynchronous operation via
await,ValueTaskcan lead to a more convoluted programming model, which can in turn actually lead to more allocations. For example, consider a method that could return either aTaskwith a cached task as a common result or aValueTask. If the consumer of the result wants to use it as aTask, such as to use with in methods likeTask.WhenAllandTask.WhenAny, theValueTaskwould first need to be converted into aTaskusingAsTask, which leads to an allocation that would have been avoided if a cachedTaskhad been used in the first place.As such, the default choice for any asynchronous method should be to return a
TaskorTask. Only if performance analysis proves it worthwhile should aValueTaskbe used instead ofTask.