IDisposable.Dispose is never called after exception in using block

1.1k views Asked by At

I understand from many sources like this and this that the Dispose method of an IDisposable will always be called if an exception is thrown in a Using block. So then I have this code:

static class MainEntryPoint
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException;

        using (var x = new Disposable())
        {
            throw new Exception("asdfsdf");
        }
    }

    private static void HandleUnhandledException(Object sender, System.UnhandledExceptionEventArgs e)
    {
        Environment.Exit(0);
    }
}

class Disposable : IDisposable
{
    public void Dispose()
    {
        System.Diagnostics.Debug.Print("I am disposed");
    }
}

It exits the application when an un-handled exception is thrown. The Dispose method is never called. Why?

3

There are 3 answers

1
Hamid Pourjam On BEST ANSWER

Environment.Exit will terminate the program

If Exit is called from a try or catch block, the code in any finally block does not execute. If the return statement is used, the code in the finally block does execute.

using (var x = new Disposable())
{
    throw new Exception("asdfsdf");
}

will be converted to

Disposable x = new Disposable();
try
{
    throw new Exception("asdfsdf");
}
finally
{
    if (x != null)
        x.Dispose();
}
0
Oladipo Olasemo On

Exactly! there is no way the code in the Dispose method will be called since Environment.Exit(0) has been called in the event handler.

Try to remove the call to Environment.Exit(0) and see if the Debug.Print() is called.

0
xanatos On

Note that if you had added a finalizer to Disposable, like:

public class Disposable : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        Console.WriteLine("I am disposed");

        if (disposing)
        {
            GC.SuppressFinalize(this);
        }
    }

    ~Disposable()
    {
        Dispose(false);
    }
}

(so using the "full" IDisposable pattern), then "normally" the finalizer would be called (because the finalizers have a chance to run on Environment.Exit), and that method would call Dispose(bool disposing). Note that even here there is the possibility that the finalizer wouldn't be run, because there is a time limit for them to run, see.