How to work with IAsyncDisposable in .NET 6

Take advantage of the IAsyncDisposable interface to dispose of objects in a non-blocking way and make your .NET applications more efficient.

Dispose and Finalize are two methods for releasing resources held by .NET and .NET Core applications executing in the context of the CLR. If your application contains unmanaged resources, you must write the necessary code to release those resources explicitly.

Since finalization is non-deterministic and finalizers are expensive in terms of resource consumption, the Dispose method is preferred over a finalizer. You can use the Dispose method on any type that implements the IDisposable interface. Eventually, with the advent of asynchronous programming, .NET needed an asynchronous counterpart to IDisposable. Thus the IAsyncDisposable interface was introduced.

This article discusses the IAsyncDisposable interface and how to work with it in C#. To work with the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here.

Create a console application project in Visual Studio 2022

First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2022 is installed in your system, follow the steps outlined below to create a new .NET Core console application project.

  1. Launch the Visual Studio IDE.
  2. Click on “Create a new project.”
  3. In the “Create a new project” window, select “Console App” from the list of templates displayed.
  4. Click Next.
  5. In the “Configure your new project” window shown next, specify the name and location for the new project.
  6. In the “Additional Information” window, select .NET 6.0 as the runtime and click Next
  7. Click Create.

This will create a new .NET Core console application project. We’ll use this project to work with the IAsyncDisposable interface in the subsequent sections of this article.

From IDisposable to IAsyncDisposable

The IDisposable interface has been around since the early days of the .NET Framework, from .NET Framework 1.0 to be precise. You probably have used this interface often when designing your classes in .NET and .NET Core. If your class implements the IDisposable interface, it is advisable that you should call the Dispose method explicitly.

However, many new features have been added to .NET Framework over the years. Because multithreading was resource intensive, asynchronous programming was added to the mix. Asynchronous programming can improve the performance and responsiveness of your application because the calling thread can continue to perform other operations while the method called asynchronously continues to execute.

Types that implement the IDisposable interface free up resources synchronously and therefore can block other threads running on the system. Moreover, if you fail to dispose an asynchronous disposable resource, it could lead to deadlocks as well. The IAsyncDisposable interface addresses the need for freeing up resources asynchronously.

When should you use IAsyncDisposable?

You should use IAsyncDisposable only when you have a class that needs to release resources asynchronously. In other words, you should use IAsyncDisposable if your class (or any of its subclasses) allocates resources that also implement IAsyncDisposable. In which case, you should ensure that the DisposeAsync method is declared as virtual in the base class.

As an example, you should take advantage of IAsyncDisposable when you’re working with asynchronous streams and instances of unmanaged resources that are resource-intensive and should be cleaned up. You can leverage the DisposeAsync method of this interface to release such resources.

Implementing the IAsyncDisposable interface

The IAsyncDisposable interface defines the DisposeAsync method. Note that the DisposeAsync method returns a ValueTask which represents an asynchronous dispose operation. Note that any non-sealed class, i.e., any class that can be extended, should have an additional method called DisposeAsyncCore that also returns a ValueTask.

A typical DisposeAsync implementation should look like this:

public async ValueTask DisposeAsync()
{
    // Perform async cleanup here
    await DisposeAsyncCore();
    // Dispose all unmanaged resources
    Dispose(false);
    GC.SuppressFinalize(this);
}

So, you can write your asynchronous code as in the example given below, and the objects of the operation will be disposed asynchronously.

await using (SqlConnection dbConnection = new SqlConnection(connectionString))
{
  // The connection instance will be disposed asynchronously when the
  // program encounters the end of the using block.
}

Note the await keyword used before the using statement in the preceding code snippet. The await keyword here informs the using block to call DisposeAsync and not Dispose when the control reaches the end of the using block.

When should you implement both IAsyncDisposable and IDisposable?

Microsoft recommends that you implement both IAsyncDisposable and IDisposable interfaces in your code. You should not consider IAsyncDisposable as a replacement for the IDisposable interface. Instead, you should consider IAsyncDisposable to be just another way of implementing the dispose pattern.

If you don’t implement the Dispose method, any code that doesn’t run in the async context will block on the DisposeAsync method so that the DisposeAsync method can be executed synchronously. Additionally, when you’re using IoC (inversion of control) containers and if your container has been disposed synchronously, a runtime exception might be thrown because the DisposeAsync method will never be called.

As Microsoft states in the .NET documentation:

It is typical when implementing the IAsyncDisposable interface that classes will also implement the IDisposable interface. A good implementation pattern of the IAsyncDisposable interface is to be prepared for either synchronous or asynchronous dispose. All of the guidance for implementing the dispose pattern also applies to the asynchronous implementation.

Implement synchronous and asynchronous dispose in .NET

The following code listing demonstrates how you can implement a synchronous and an asynchronous dispose pattern in your code:

public class Example : IDisposable, IAsyncDisposable
{
    private FileStream fileStream =
    new FileStream("D:\\test.txt", FileMode.Create);
    public void Dispose()
    {
        // Write code here to dispose resources synchronously
        fileStream.Dispose();
    }
    public async ValueTask DisposeAsync()
    {
        // Write code here to dispose resources asynchronously
        await fileStream.DisposeAsync();
    }
}

The System.IAsyncDisposable interface was released with C# 8.0. Similar to IDisposable, you should dispose IAsyncDisposable objects at the end of an HTTP request. You should call GC.SupressFinalize(this) in your Dispose or DisposeAsync method so that the garbage collector doesn’t need to call the destructor later.

Copyright © 2022 IDG Communications, Inc.

How to choose a low-code development platform