The Quick Notes

Built with caffeine, curiosity, and a suspicious number of tabs

Use ValueTask<T> in .NET

Truc Nguyen

Keywords: tech, c#, dotnet, async

Abstract

A quick note on when and why to use ValueTask<T> instead of Task<T> in .NET for high-throughput, cache-friendly async APIs.

Use ValueTask<T> when your async method often returns a result synchronously — it avoids unnecessary heap allocations and is faster under load.


The problem with Task<T>

Every Task<T> is a heap-allocated object. For methods that resolve synchronously most of the time (e.g., cache hits), this creates a lot of garbage for no real benefit.

The fix with ValueTask<T>

ValueTask<T> is a struct. When the result is already available, it returns inline — zero allocation.

internal interface ILookupService
{
    ValueTask<IList<LookupModel>> GetCategoriesAsync();
}

When to use it

ScenarioUse
Result often comes from cache (sync)ValueTask<T>
Always awaits I/O (DB, HTTP)Task<T>
Called in loop that service mostly returns sync resultsValueTask<T>

Don't use ValueTask<T> everywhere — only where performance profiling shows benefit