🎓 A1Learner
🔍
0 of 28 lessons completed

C# ValueTask

A ValueTask<T> is a return type for an operation that often finishes with a result already in hand. It can carry the ready result itself or wrap a Task for the slower path.

A Result That Is Ready

The GetScore method builds a ValueTask from a plain value. Main awaits it and prints the value.

using System;
using System.Threading.Tasks;

class Program
{
    static ValueTask<int> GetScore()
    {
        return new ValueTask<int>(42);
    }

    static async Task Main(string[] args)
    {
        int score = await GetScore();
        Console.WriteLine(score);
    }
}

Cached and Fresh

The Lookup method returns a ready ValueTask for a cached name. Any other name takes the Task-backed path.

using System;
using System.Threading.Tasks;

class Program
{
    static async Task<int> FreshValue()
    {
        await Task.CompletedTask;
        return 5;
    }

    static ValueTask<int> Lookup(string name)
    {
        if (name == "cached")
        {
            return new ValueTask<int>(3);
        }

        return new ValueTask<int>(FreshValue());
    }

    static async Task Main(string[] args)
    {
        Console.WriteLine(await Lookup("cached"));
        Console.WriteLine(await Lookup("fresh"));
    }
}

Convert, Then Await Twice

Main converts one ValueTask into a Task with AsTask. The Task is then awaited twice, and both awaits print the value.

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        ValueTask<int> value = new ValueTask<int>(9);
        Task<int> task = value.AsTask();

        Console.WriteLine(await task);
        Console.WriteLine(await task);
    }
}

Example

using System;
using System.Threading.Tasks;

class Program
{
    static async Task<int> LoadPrice()
    {
        await Task.CompletedTask;
        return 30;
    }

    static ValueTask<int> Price(string item)
    {
        if (item == "pen")
        {
            return new ValueTask<int>(20);
        }

        return new ValueTask<int>(LoadPrice());
    }

    static async Task Main(string[] args)
    {
        Console.WriteLine(await Price("pen"));
        Console.WriteLine(await Price("book"));
    }
}

Key points

  • A method returns a ValueTask<T> with new ValueTask<T>(value) when the result is ready at once.
  • Awaiting a ValueTask<T> gives the value, just like awaiting a Task<T>.
  • A ValueTask<T> can also wrap a Task<T> for a result that is not ready yet.
  • The ready path avoids a separate Task object for that result.
  • A ValueTask may be awaited only once.
  • The AsTask method converts a ValueTask into a Task.
  • A converted Task can be awaited more than once.

In this example, Price returns a ready ValueTask with 20 for the pen. The book takes the slower path and wraps the LoadPrice task, which returns 30. Main awaits each lookup once and prints the prices in call order.

Note

System.Threading.Tasks provides both Task and ValueTask.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What should code do when a ValueTask result must be awaited twice?