Common Type System—Unhandled Constructor Exceptions
Unhandled Constructor Exceptions
An unhandled exception from a constructor can occur if the body of a constructor causes an exception to be raised. This is sometimes unavoidable, although it is inconvenient. For example, if you were unable to fully initialize the object, for example because of inconsistent or corrupt state, you would have no other choice but to throw an exception. The alternative would be to pretend everything was OK by returning successfully from the constructor, which could lead to failures later on in the program’s execution. It’s better just to have the program fail as soon as you notice a problem.
The reason that this topic is problematic enough to call out is that, in the face of an exception thrown by
a constructor, the caller of your constructor will have no reference to your object. Consider what the IL
sequence looks like for the C# code Foo f = new Foo():
newobj instance void Foo::.ctor() stloc.0
The newobj instruction makes a call to the constructor, leaving the result on the execution stack. stloc.0
stores it in a local slot in the execution stack. But if an exception is raised as a result of the newobj instruction
(because of insufficient memory to allocate the object, or because of an unhandled exception thrown from
the constructor), the result will never be placed on the stack, and the stloc.0 will never execute because the
CLR exception handling behavior will take over. The result is that the variable f will be null.
This can cause some unpredictable behavior. For example, consider a type that allocates resources in its
constructor but then throws an exception. Normally, types like this will support the IDisposable
interface—explained fully in Chapter 5—so that these resources can be reclaimed deterministically.
Such code might look like this:
using (Foo f = new Foo()) { // Do something interesting... }
The semantics of the using statement are such that the variable inside the parenthesis will have its Dispose
method called at the end of the block, regardless of whether exit is normal or due to an exception from the
block’s body. But if an exception occurs during the call to Foo’s constructor, f will never get assigned a
value! The result is that Dispose will never get called, and hopefully f’s finalizer will clean up the
resources sometime later on.
|

