Prefer ValueTask<T> for Interfaces


Declaring interfaces for your .NET classes gives you the choice: synchronous or asynchronous. Why not both?

Using Task for interfaces

Let's look at this interface:

interface IProductRepo {
  IEnumerable<Product> GetProducts();

The GetProducts method returns some collection of products.

However, this interface declaration is restrictive, meaning that we should implement this method synchronously. When talking to a database this will block the current thread which we don't want.

The alternative? We can use Task<T>:

interface IProductRepo {
  Task<IEnumerable<Product>> GetProductsAsync();

Again, this is restrictive because this method should always return a Task<T>, which being a reference type results in the allocation of a new Task on the heap, even when implementing this method synchronously.

Is there a way we can declare an interface to support both a synchronous and asynchronous implementation?

Using ValueTask for interfaces

Yes, there is! We can use ValueTask<T>! This type represents the union of T and Task<T> and allows you to implement a method synchronously or asynchronously.

The interface should now look like this:

interface IProductRepo {
  ValueTask<IEnumerable<Product>> GetProductsAsync();

ValueTask<T> is a value type, so when implementing this method synchronously this will result in no heap allocation for Task<T>. This will also allow you to implement the GetProductsAsync method asynchronously which does result in a Task<T> allocation on the heap.

ValueTask<T> gives you the choice! And the .NET runtime takes care of optimization.

Do note that the method should be named GetProductsAsync because you should invoke this using the await syntax!