C# › Asynchronous Programming

What is ValueTask<T> in C#, and when does it actually provide a meaningful performance benefit over Task<T>?

Advanced

Answer

Task<T> always allocates an object on the heap, even when a method's result is already available synchronously; ValueTask<T> is a struct that can represent an already-completed result without any heap allocation at all, avoiding that overhead specifically in the common case where a value is often available immediately (like a cache hit) rather than genuinely requiring asynchronous work -- the trade-off is that a ValueTask<T> should be awaited only once and never awaited multiple times or accessed concurrently, since it doesn't guarantee the same safe, repeatable semantics a Task<T> does.
// Task<T>: always allocates, even for an already-known result
public async Task<int> GetValueAsync() => await Task.FromResult(42);

// ValueTask<T>: avoids the allocation entirely when the result is already available
public ValueTask<int> GetValueAsync() => new ValueTask<int>(42);

// If genuine async work is sometimes needed, it can still wrap a real Task<T>
public ValueTask<int> GetCachedOrFetchAsync(string key) =>
    _cache.TryGetValue(key, out int value) ? new ValueTask<int>(value) : new ValueTask<int>(FetchAsync(key));
Real-world example A caching layer's GetAsync method returns ValueTask<T> specifically because the overwhelming majority of calls are cache hits resolved synchronously, avoiding a heap allocation on every single cache lookup across a very high-frequency, latency-sensitive code path.

Common follow-ups: What specific bug can occur if a ValueTask<T> is awaited more than once?;In what scenario would using ValueTask<T> actually hurt readability or correctness enough to not be worth the optimization?