Dispose vs Exception handling

Exploring the sequence of event when using a disposable class inside a try-catch block

Home DailyDrop

Daily Knowledge Drop

When using a class which implements IDisposable inside a try-catch-finally block, if an exception is thrown in what order are the Dispose and catch block executed?

The order is:

  1. Dispose method on the class implementing IDisposable
  2. Catch block
  3. Finally block

Logically this makes sense, as the IDisposable class is out of scope when the catch block is being executed. The lowered code can also be viewed to get an even deeper understanding of how these two features fit together.


Example

Consider the following class which implements IDisposable:

public class DisposableClass : IDisposable
{
    public DisposableClass()
    {
        Console.WriteLine($"In Constructor of {nameof(DisposableClass)}");
    }

    public void Dispose()
    {
        Console.WriteLine($"In Dispose of {nameof(DisposableClass)}");
    }
}

As it implements IDisposable, it can be used with the using statement:

try
{
    // declare instance of the disposable class
    using var disposableInstance = new DisposableClass();

    throw new Exception("Exception or dispose?");

}catch(Exception ex)
{
    Console.WriteLine("An exception occurred");
}
finally
{
    Console.WriteLine("In the finally block");
}

Executing the above, results in the following output:

In Constructor of DisposableClass
In Dispose of DisposableClass
An exception occurred
In the finally block

Dispose called before the catch block.


Lowered

Looking at the lowered code (generated using sharplab.io) it becomes more obvious as to why this is the sequence of events:

internal static class <Program>$
{
    private static void <Main>$(string[] args)
    {
        try
        {
            DisposableClass disposableClass = new DisposableClass();
            try
            {
                throw new Exception("Exception or dispose?");
            }
            finally
            {
                if (disposableClass != null)
                {
                    ((IDisposable)disposableClass).Dispose();
                }
            }
        }
        catch (Exception)
        {
            Console.WriteLine("An exception occurred");
        }
        finally
        {
            Console.WriteLine("In the finally");
        }
    }
}
internal class DisposableClass : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Disposing DisposableClass");
    }
}

The using statement is converted into its own try-finally block, with the Dispose being called inside the finally.

Looking at the above code, it make entire sense that the sequence of events is:

  1. Constructor of the disposable class
  2. The first finally block is called, and the Dispose method is invoked
  3. The catch block is called, and the exception is handled
  4. The second finally block is called

Notes

The sequence of events in this scenario was not something I had considered before - but thinking through what each block of code does (as well as executing the sample code, and looking at the lowered code), the sequence of events makes sense, and is as one would probably expect. However, it is good to get confirmation and gain a better understanding of how one's code might operate under the hood.


References

Davide Bellone tweet

Daily Drop 151: 01-09-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net disposable idisposable exception