The IAsyncDisposable interface in .NET Core 3.0
Implementing IAsyncDisposable on disposable objects.
.NET Conf 2019 Countdown series
I’m excited to be part of the .NET Conf with this every day mini-post series until the 23th September.
- IL-Linker in .NET Core 3.0
- The Bundler in .NET Core 3.0
- Crossgen as build step with .NET Core 3.0
- The IAsyncDisposable interface in .NET Core 3.0
- Platform intrinsics in .NET Core 3.0
- .NET Conf 2019 is right ahead
Prerequisites & Setup
The IAsyncDisposable interface
Every .NET developer will sooner or later use the
Dispose() method (indirectly with a
using statement) and implement the
The pattern for disposing an object, has been available as a concept with .NET for a long time, going back to the .NET Framework 1.1.
Since then, a lot has evolved - new things like
async/await were added and multi-threaded systems are no longer indispensable and more important nowadays with the cloud. Thus there is a need to do the disposal also asynchronously.
Sure, you could do the following to dispose an object asynchronously in a
async/await context (not recommended):
Wrongly usage of
Task.Run() could cause thread pool starvation - thread pool threads, are a globally shared resource.
Beside that, you couldn’t use
disposableObj in a
The new interface (IAsyncDisposable) helps.
DisposeAsync() fulfills exactly the same purpose as the
Dispose() method of IDisposable and should follow the smae implementation rules:
- DisposeAsync/Dispose could be called multiple times, subsequent calls must be ignored
- DisposeAsync/Dispose shouldn’t throw exception
- DisposeAsync/Dispose must be implemented when it holds another disposable object or/and unmanaged resources
using statement would be like this:
Similar to the non await using statement, it expands to (internally):
Well, let’s see how you could implement
IAsyncDisposable on a class that does already implementing
IDisposable in your own library:
Usually, you are good to go by calling the existing dispose method, but this isn’t always possible - it depends on the structure/purpose of your class. You maybe wonder, why
DisposeAsync is returning a ValueTask and not a
Task - It’s because of performance.
ValueTask is a struct that doesn’t add pressure to the GC by allocating on the heap. This could be important when many objects getting disposed in a tight loop. The
try/catch is only required when the class isn’t sealed or/and you aren’t sure that under no circumstances an exception is thrown.
Further, there is no
CancellationToken support for
DisposeAsync because there is no well-known scenario to cancel cleanup process and leave the object in an inconsistent state.
IAsyncDisposable is not inherited from
IDisposable by intension, allowing developers to choose between implementing one or both. Depending on your class usage/purpose, it’s more appropriate to offer only
DisposeAsync when it is likely used in an asynchronously fashion.
Another benefit of having
IAsyncDisposable is to perform a resource-intensive dispose operation without blocking the main thread of a GUI application for a long time.
Last but not least: this exists in the .NET Standard 2.1 and later - implemented by the .NET Core 3.0.
As you maybe already know, the “Windows” .NET Framework (e.g. 4.7.2, 4.8 and so on) will never implement .NET Standard 2.1 and thus no
Overall, I think the
IAsyncDisposable interface and the
await keyword for the
using statement is a meaningful extension.
It rounds off the
async/await programming model that is meanwhile used a lot (
DbContext.SaveChangesAsync(...), just to name few).
Feel free to leave a comment - I’m always happy to get feedback of any kind.