What happens if a constructor throws an exception? Is a new object still created? Do we even need to care?

Yes, a new object is created. It won’t be initialized properly and we usually won’t have a reference to it. And yes, we do need to care. That half initialized object may block important resources.

The examples in this series are in C#, but everything can be applied to Java and other similar object oriented languages.

Consider the following class

class TwoResources
{
    private Stream fileA;
    private Stream fileB;

    public TwoResources(string fileNameA, string fileNameB)
    {
        fileA = File.Open(fileNameA, FileMode.Open);
        fileB = File.Open(fileNameB, FileMode.Open);
    }

    public void Close()
    {
        if (fileA != null)
        {
            fileA.Close();
        }
        if (fileB != null)
        {
            fileB.Close();
        }
    }

    ... some methods
}

The constructor can throw exceptions if either of the two files could not be opened. Now consider this naive use of the TwoResources class:

TwoResources instance = null;
try
{
    // FILE_A exists
    // FILE_X does not exist
    instance = new TwoResources(FILE_A, FILE_X);
}
catch (Exception ex)
{
    Debug.Assert(instance == null);
    // FILE_A and FILE_B exist
    instance = new TwoResources(FILE_A, FILE_B);
}

The constructor inside the try block will throw an exception because the second file doesn’t exist. The catch block then goes for the next best option and tries to open two files that actually do exist. However, this call to the constructor will also throw an exception.

This is because the first instance of TwoResources has actually been created and holds on to FILE_A. This means that the first instance will block that file for all processes until it is garbage collected. Actually it is worse: because the TwoResources class does not have a destructor that closes the two streams, we will have to wait until the Stream object is garbage collected.

To examine the half-initialized object, we can leak it in the constructor:

class TwoResources
{
    public static TwoResources lastInstance;
    private Stream fileA;
    private Stream fileB;

    public TwoResources(string fileNameA, string fileNameB)
    {
        lastInstance = this;
        fileA = File.Open(fileNameA, FileMode.Open);
        fileB = File.Open(fileNameB, FileMode.Open);
    }
    ... some methods
}

This is what our half initialized instance looks like once we have reached the catch block:

A half initialized instance

It’s easy to see that a TwoResources object was indeed created. One Stream object was also created, but the second one wasn’t.

Cleaning up an object where the constructor threw an exception is not straightforward. Doing the clean-up in the destructor still means that we have to wait until the object is garbage collected. This might never happen – or too late. Providing a clean-up method like Close() will not work because we don’t have a reference to the object (ignoring our evil leak of “this” which will only make the problem worse in real life).

So there you have it: you do have to care about throwing exceptions in constructors if resources are involved. You could leave a mess that can only be resolved reliably by restarting your application.

In my next posts I will discuss a few patterns that can help you write safe constructors (and destructors).

Part 2: Open and Close

Advertisements