Take advantage of ValueTask in C# to avoid allocation when returning task objects from an asynchronous method Credit: graemenicholson / Getty Images Asynchronous programming has been in use for quite some time now. In recent years, it has been made more powerful with the introduction of the async and await keywords. You can take advantage of asynchronous programming to increase your application’s responsiveness and throughput. The recommended return type of an asynchronous method in C# is Task. You should return Task if you would like to write an asynchronous method that returns a value. If you would like to write an event handler, you can return void instead. Until C# 7.0 an asynchronous method could return Task, Task, or void. Beginning with C# 7.0, an asynchronous method also can return ValueTask (available as part of the System.Threading.Tasks.Extensions package) or ValueTask. This article presents a discussion of how we can work with ValueTask in C#. To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here. Create a .NET Core console application project in Visual Studio First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new .NET Core console application project in Visual Studio. Launch the Visual Studio IDE. Click on “Create new project.” In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed. Click Next. In the “Configure your new project” window shown next, specify the name and location for the new project. Click Create. This will create a new .NET Core console application project in Visual Studio 2019. We’ll use this project to illustrate the use of ValueTask in the subsequent sections of this article. Why should I use ValueTask? A Task represents the state of some operation, i.e., whether the operation is completed, cancelled, and so on. An asynchronous method can return either a Task or a ValueTask. Now, since Task is a reference type, returning a Task object from an asynchronous method implies allocating the object on the managed heap each time the method is called. Thus, one caveat in using Task is that you need to allocate memory in the managed heap every time you return a Task object from your method. If the result of the operation being performed by your method is available immediately or completes synchronously, this allocation is not needed and therefore becomes costly. Here is exactly where ValueTask comes to the rescue. ValueTask provides two major benefits. First, ValueTask improves performance because it doesn’t need heap allocation, and second, it is both easy and flexible to implement. By returning ValueTask instead of Task from an asynchronous method when the result is immediately available, you can avoid the unnecessary overhead of allocation since “T” here represents a structure and a struct in C# is a value type (in contrast to the “T” in Task, which represents a class). Task and ValueTask represent two primary “awaitable” types in C#. Note that you cannot block on a ValueTask. If you need to block you should convert the ValueTask to a Task using the AsTask method and then block on that reference Task object. Also note that each ValueTask can be consumed only once. Here the word “consume” implies that a ValueTask can asynchronously wait for (await) the operation to complete or take advantage of AsTask to convert a ValueTask to a Task. However, a ValueTask should be consumed only once, after which the ValueTask should be ignored. ValueTask example in C# Suppose you have an asynchronous method that returns a Task. You might take advantage of Task.FromResult to create the Task object as shown in the code snippet given below. public Task<int> GetCustomerIdAsync() { return Task.FromResult(1); } The above code snippet does not create the entire async state machine magic but it allocates a Task object in the managed heap. To avoid this allocation, you might want to take advantage of a ValueTask instead as shown in the code snippet given below. public ValueTask<int> GetCustomerIdAsync() { return new ValueTask(1); } The following code snippet illustrates a synchronous implementation of ValueTask. public interface IRepository<T> { ValueTask<T> GetData(); } The Repository class extends the IRepository interface and implements its methods as shown below. public class Repository<T> : IRepository<T> { public ValueTask<T> GetData() { var value = default(T); return new ValueTask<T>(value); } } Here is how you can call the GetData method from Main method. static void Main(string[] args) { IRepository<int> repository = new Repository<int>(); var result = repository.GetData(); if(result.IsCompleted) Console.WriteLine("Operation complete..."); else Console.WriteLine("Operation incomplete..."); Console.ReadKey(); } Let’s now add another method to our repository, this time an asynchronous method named GetDataAsync. Here is what the modified IRepository interface would look like. public interface IRepository<T> { ValueTask<T> GetData(); ValueTask<T> GetDataAsync(); } The GetDataAsync method is implemented by the Repository class as shown in the code snippet given below. public class Repository<T> : IRepository<T> { public ValueTask<T> GetData() { var value = default(T); return new ValueTask<T>(value); } public async ValueTask<T> GetDataAsync() { var value = default(T); await Task.Delay(100); return value; } } When should I use ValueTask in C#? Albeit the benefits that ValueTask provides, there are certain trade-offs to using ValueTask in lieu of Task. ValueTask is a value type with two fields, whereas Task is a reference type with a single field. Hence using a ValueTask means working with more data since a method call would return two fields of data in lieu of one. Also, if you await a method that returns a ValueTask, the state machine for that asynchronous method would be larger as well — because it would have to accommodate a struct that contains two fields in lieu of a single reference in the case of a Task. Further, if the consumer of an asynchronous method uses Task.WhenAll or Task.WhenAny, using ValueTask as a return type in an asynchronous method might become costly. This is because you would need to convert the ValueTask to Task using the AsTask method, which would incur an allocation that could be easily avoided if a cached Task had been used in the first place. Here is the rule of the thumb. Use Task when you have a piece of code that will always be asynchronous, i.e., when the operation will not immediately complete. Take advantage of ValueTask when the result of an asynchronous operation is already available or when you already have a cached result. Either way, you should perform the necessary performance analysis before considering ValueTask. How to do more in C#: How to use immutability in C How to use const, readonly, and static in C# How to use data annotations in C# How to work with GUIDs in C# 8 When to use an abstract class vs. interface in C# How to work with AutoMapper in C# How to use lambda expressions in C# How to work with Action, Func, and Predicate delegates in C# How to work with delegates in C# How to implement a simple logger in C# How to work with attributes in C# How to work with log4net in C# How to implement the repository design pattern in C# How to work with reflection in C# How to work with filesystemwatcher in C# How to perform lazy initialization in C# How to work with MSMQ in C# How to work with extension methods in C# How to us lambda expressions in C# When to use the volatile keyword in C# How to use the yield keyword in C# How to implement polymorphism in C# How to build your own task scheduler in C# How to work with RabbitMQ in C# How to work with a tuple in C# Exploring virtual and abstract methods in C# How to use the Dapper ORM in C# How to use the flyweight design pattern in C# Related content feature What is Rust? Safe, fast, and easy software development Unlike most programming languages, Rust doesn't make you choose between speed, safety, and ease of use. Find out how Rust delivers better code with fewer compromises, and a few downsides to consider before learning Rust. By Serdar Yegulalp Nov 20, 2024 11 mins Rust Programming Languages Software Development how-to Kotlin for Java developers: Classes and coroutines Kotlin was designed to bring more flexibility and flow to programming in the JVM. Here's an in-depth look at how Kotlin makes working with classes and objects easier and introduces coroutines to modernize concurrency. By Matthew Tyson Nov 20, 2024 9 mins Java Kotlin Programming Languages analysis Azure AI Foundry tools for changes in AI applications Microsoft’s launch of Azure AI Foundry at Ignite 2024 signals a welcome shift from chatbots to agents and to using AI for business process automation. By Simon Bisson Nov 20, 2024 7 mins Microsoft Azure Generative AI Development Tools news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks Resources Videos