Search code examples
c#.netasync-awaitvaluetask

Convert a ValueTask<T> to a non generic ValueTask


The question asked here is the same as the one here and is aimed to create a definitive solution to it. The most accurate answer is by Stephen Toub himself in this issue that is exactly about this question. The "recommendended code" is the following one:

public static ValueTask AsValueTask<T>(this ValueTask<T> valueTask)
{
    if (valueTask.IsCompletedSuccessfully)
    {
        valueTask.GetResult();
        return default;
    }

    return new ValueTask(valueTask.AsTask());
}

This answer is not up-to-date - a ValueTask doesn't expose a GetResult() (only a Result property) - and THE question is:

  • Do we need to "pull" the Result out of the ValueTask (to "release" the IValueTaskSource that may operate under this ValueTask)?
  • If yes:
    • is it the .GetAwaiter() call that is missing above?
    • OR is a fake call to the property is guaranteed to work var fake = valueTask.Result;? Always? (I'm afraid of dead code elimination.)
  • If not, is a straight implementation like the following one enough (and optimal)?
public static ValueTask AsNonGenericValueTask<T>( in this ValueTask<T> valueTask )
{
    return valueTask.IsCompletedSuccessfully ? default : new ValueTask( valueTask.AsTask() );
}

Solution

  • What's missing in that code is .GetAwaiter():

    public static ValueTask AsValueTask<T>(this ValueTask<T> valueTask)
    {
        if (valueTask.IsCompletedSuccessfully)
        {
            valueTask.GetAwaiter().GetResult();
            return default;
        }
    
        return new ValueTask(valueTask.AsTask());
    }
    

    You're partially right in that you don't care about the result. But you might care about a thrown exception or cancellation that you'll miss if you don't query the result.

    Or you can write it like this:

    public static async ValueTask AsValueTask<T>(this ValueTask<T> valueTask)
        => await valueTask;