What is the difference between value tasks and tasks in C#?

Answer

Task<T> is a heap-allocated reference type — even for synchronous results, a Task object is created. In hot paths called millions of times per second (e.g., cache lookups), this causes significant GC pressure. ValueTask<T> (C# 7+) is a struct that can be either synchronous (no allocation) or backed by a real Task when truly asynchronous. If the operation completes synchronously (common in cached scenarios), ValueTask<T> avoids the heap allocation entirely. When to use: Use Task<T> by default. Switch to ValueTask<T> when: (1) the method is called in a very hot path, (2) the synchronous path is the common case, and (3) profiling shows allocation pressure. Important constraints: ValueTask must be awaited only once — do not cache, pass around, or await multiple times. For shared awaiting, call .AsTask() first. IValueTaskSource<T> enables pooling-based zero-allocation async operations (used by .NET runtime internally for socket I/O).