---
title: "Task vs ValueTask - The what's, when's and how's"
description: "Since .NET Core 2.0 the Type `ValueTask`. It seems that there is a lot of overlap between `Task` and `ValueTask`.\nSo let's have a deeper look into `ValueTask`. Where should we use it and how should we use it properly? And also: where we should **not** use is."
updated: 2021-12-05
tags: ["ValueTask", "async", "Task", "C#"]
canonical: "https://steven-giesel.com/blogPost/28ed9a4d-c390-46f1-ad83-8fda4747ed1f/task-vs-valuetask-the-whats-whens-and-hows"
---

# Task vs ValueTask - The what's, when's and how's

## Recap
If you need a small refresher how Task or better `async` / `await` works I got you covered.
Here [my blog post](blogPost/e0eec540-24db-471c-b8fb-1df8530911d6) or here the [YouTube video from the talk itself](https://www.youtube.com/watch?v=oCYEQhIUoMw).

In the later part of the talk I advise against using `ValueTask`. Especially because there are some pitfalls you have to be careful of. Time passed and I re-advised my own comment / advise. In short to a certain degree I still stand against using `ValueTask` everywhere but there are use-cases and depending on the usage it is totally fine.

## **What** is a `ValueTask`
Let's oversimplify for the first part. How does `ValueTask` look like?
```csharp
public readonly struct ValueTask<TResult>
{
    internal readonly Task<TResult> _task;
    internal readonly TResult _result;
}
```

Now there are two things, which are important:
 
1. It itselfs wraps `TResult` and `Task<TResult>`
2. In contrast to `Task` `ValueTask` is a `readonly struct` instead of a `class`

Let's unwrap that one by one

### Wrapping `TResult` and `Task<TResult>`
The idea behind `ValueTask` is that you can await asynchronous **and** synchronous stuff. Here a small code sample: 
```csharp
public class WeatherService
{
    private readonly Dictionary<string, WeatherData> _weatherCache;
    private readonly IWeatherRepository weatherRepository;

    public async ValueTask<WeatherData> GetWeatherForCityAsync(string city)
    {
        if (_weatherCache.ContainsKey(city))
        {
            return _weatherCache[city];
        }		

        return await weatherRepository.GetForCityAsync(city);
    }
}
```

We have two code path. One is asynchronous (when we have to call the `WeatherRepository`) and one is synchronous (when we hit the cache).
That is the classic use case for `ValueTask`. In some execution paths we don't have any asynchronous call and therefore we don't want to have the overhead of the Task-object. That is why `ValueTask` has both fields, one `Task<T>` for an asynchronous call-path and `T` for the synchronous code path.
But why avoiding `Task.FromResult` and introducing a whole new class / concept? That brings us directly to the second point

### `readonly struct` for `ValueTask`
Taking the example from before. Just imagine we're calling `await GetWeatherForCityAsync("Zurich")` and our cache already holds the data?
Well we avoid the allocation of `Task`.
And that is the whole point. We are avoiding allocation of a class. We all know that doesn't bring that much boost in performance. And that is why also Microsoft's state that `ValueTask` is mainly meant for "hot paths".
> This is why ValueTask<TResult> was added to .NET Core 2.0, and why new methods that are expected to be used on hot paths are now defined to return ValueTask<TResult> instead of Task<TResult>. For example, when we added a new ReadAsync overload to Stream in .NET Core 2.1 in order to be able to pass in a Memory<byte> instead of a byte[], we made the return type of that method be ValueTask<int>. 

Source can be found [here](https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/)

Now that sounds good so far. But our story doesn't end here. There are some pitfalls with `ValueTask`. 

## Asynchronous hot paths
Now the stuff we discussed until now helps optimizing code which runs eventually synchronous but the .NET team went further and also optimized the allocation in the asynchronous call path. We know that a `Task` object has to be created on the heap as it is a `class`. But this doesn't mean that every awaitable Task has to be created newly. And there is where the magic of `ValueTask` kicks in a second tine. Again it is all about optimizing hot paths.

## Big No-No's
The following operations should never be performed on a ValueTask instance:

### Awaiting the instance multiple times
We saw that `ValueTask`s get recycled. Awaiting multiple times could mean you await a recycled `ValueTask` with unknown outcome
```csharp
ValueTask<WeatherData> weatherData = weatherService.GetWeatherForCityAsync("Zurich");
var result1 = await weatherData;
var result2 = await weatherData;
``` 
### Awaiting the same instance concurrently
Also because of internal design `ValueTask` only expects a single callback
```csharp
ValueTask<WeatherData> weatherData = weatherService.GetWeatherForCityAsync("Zurich");
Task.Run(async() => await weatherData);
Task.Run(async() => await weatherData);
```

### Using .Result or .GetAwaiter().GetResult() when the operation hasn't yet completed, or using them multiple times
Not used so often, but still this will lead to big problems. 

## When to use `ValueTask`
Via default and if in doubt use `Task`. `ValueTask` is meant for hot paths. I personally use it also in some Repository patterns with multiple implementations.
In fact that very page you are looking at calls a `ValueTask` function to get the blog post content due to a cached repository which has `sync` and `async` code paths.
```csharp
interface IRepository<T>
{
    ValueTask<T> GetAll();
}
```

**Summarizing**: 

If you for sure know that your caller will only `await` your `ValueTask` once, it is also fine to offer that Type. 

If you need the allocation free version due to your requirements.

If you expect synchronous and asynchronous completion in your code.


But in general I would always check your code with a benchmark first. Don't add `ValueTask` just because you could save some byte and a few nano seconds. You can introduce serious bugs. Furthermore if you offer an `abstract class` or `interface` account for inheritance and other factors. When in doubt take `Task`.
