JAL Computing

C++COMProgramming .NET Mac Palm CPP/CLI Hobbies

 

Home
Up

Chapter 30 "Exceptional Construction"

In standard C++, if an exception is thrown in the constructor the object never existed and the destructor will not be called. This makes sense since you cannot call the destructor on an object if the object does not exist! Knowing that the destructor will only be called on a completely constructed object is integral to the RAII (Resource Allocation Is Initialization) pattern. In fact, this guarantee is seen in many languages, but not in Java or C#. Since the destructor in C# can be called on an incompletely constructed object, releasing resources in the destructor is a more challenging task.

In C#, the destructor maps to the finalizer and the finalizer is called when the object is garbage collected. If you wish to implement deterministic cleanup in C# the proper idiom is to implement the IDisposable interface and create the disposable object within the using construct. This guarantees that Dispose will be called in a deterministic manner even if an exception is thrown in the body of the using construct. 

using (Program p = new Program()) {
    p.SayHello(); // Dispose will be called even if an exception is thrown here
}

Q:

What happens if an exception is thrown in the constructor of the disposable object? 

A:

If you come from C++ or even PHP, not what you think! In C#, if an exception is thrown in the constructor the finalizer will still be called when the garbage collector executes! This can lead to unexpected behavior if you clean up unmanaged resources in the finalizer. For instance, if you increment a reference counter in the constructor and decrement the reference counter in the finalizer, the reference counter will probably still be decremented on exceptional failed construction! This can leave the system in an invalid state. So, C++ coders Be Warned, C# and C++/cli both exhibit unexpected behavior when an exception is thrown in the constructor. The destructor will still normally be called on the incompletely constructed object. The C# coder will need to program defensively since there is no guarantee that resources were acquired in the constructor.

Note: Calling the finalizer is not guaranteed if an exception is thrown in the finalizer list.

Q:

What happens if an exception is thrown in the constructor of a disposable object within the using construct? 

A:

Dispose() will not be called, but the finalizer will be called when the object is garbage collected. This seems reasonable since the using block is never entered.

Here is the test code that I used to establish this behavior:

using System;
using System.Collections.Generic;
using System.Text;
namespace TestException
{
    class Program : IDisposable
    {
        private bool disposed = false;
        public Program()
        {
            Console.WriteLine("Constructor called.");
            throw new ArgumentException();
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this); // remove this from gc finalizer list
            Console.WriteLine("Dispose() called.");
        }
        // call Dispose(true) from Dispose, call Dispose(false) from finalizer
        // dispose once only
        private void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing) // called from Dispose
                {
                    // Dispose managed resources.
                }
                // Clean up unmanaged resources here.
            }
            System.Console.WriteLine("Dispose(bool) called");
            disposed = true;
        }
        ~Program() // maps to finalize
        {
            Dispose(false);
        }
        // ASSERT disposed is false
        public void SayHello()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Program");
            }
            Console.WriteLine("Hello");
        }
        static void Main(string[] args)
        {
            try
            {
                using (Program p = new Program()) {
                    p.SayHello();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            System.GC.Collect();
            Console.ReadLine();
        }
    }
}

Both C++ and PHP guarantee that the destructor will not be called on an incompletely constructed object. Not so in C#. IMHO, this is illogical. If the user defined constructor code does not execute to completion I see no reason to call the user defined destructor code. The C# coder using the RAII pattern must recognize this shortcoming and code appropriately.

In the C# newsgroup:

Willy D. suggest calling GC.SuppressFinalize(this) in the constructor on exception as in:

class Foo {
    public Foo()
    {
        try {
            FunctionThatCanThrow();
        }
        catch(Exception) {
            GC.SuppressFinalize(this);
            throw;
        }
        resourceHandle = ....// allocate unmanaged resource that needs clean-up.
    }
    ~Foo()
    {
        if(resourceHandle)
            ... // clean-up
    }

MSFT suggest separating construction and resource acquisition:

In addition to the comments and options Jon and Willy offered, you can also 
separate the construction of the object from the acquisition of resources 
into two separate phases so that the constructor never throws an exception. 
In the constructor set fields to known values (null, etc) to establish its 
invariants, then use an Initialization method to acquire the resources.
FYI: If an exception is thrown from a static constructor then that type is 
thereafter unusable in that appdomain - you will not be able to create 
instances of that type in that AppDomain.

 

 

Send mail to [email protected] with questions or comments about this web site. Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 © 
Last modified: 08/04/09
Hosted by www.Geocities.ws

1