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!