Chapter 7. Exception Handling and Exception Safety

The CLR contains strong support for exceptions. Exceptions can be created and thrown at a point where code execution cannot continue because of some exceptional condition (usually a method failure or an invalid state). Writing exception-safe code is a difficult art to master. It would be a mistake to assume that the only tasks required when writing exception-safe code are simply throwing exceptions when an error occurs and catching them at some point. Such a view of exception-safe code is shortsighted and will lead you down a path of despair. Instead, exception-safe coding means guaranteeing the integrity of the system in the face of exceptions. When an exception is thrown, the runtime will iteratively unwind the stack while cleaning up. Your job as an exception-safe programmer is to structure your code in such a way that the integrity of the state of your objects is not compromised as the stack unwinds. That is the true essence of exception-safe coding techniques.

In this chapter, I will show you how the CLR handles exceptions and the mechanics involved with handling exceptions. However, there is more to exception handling than just that. For example, I'll describe which areas of code should handle exceptions as well as pitfalls to avoid when implementing exception handling. Most importantly, I will show you how writing exception-safe code may not even involve handling exceptions at all. Such code is typically called exception-neutral code. It may sound surprising, but read on for all of the details.

How the CLR Treats Exceptions

Once an exception is thrown, the CLR begins the process of unwinding the execution stack iteratively, frame by frame.[18] As it does so, it cleans up any objects that are local to each stack frame. At some point, a frame on the stack could have an exception handler registered for the type of exception thrown. Once the CLR reaches that frame, it invokes the exception handler to remedy the situation. If the stack unwinds completely and a handler is not found for the exception thrown, then the unhandled-exception event for the current application domain may be fired and the application could be aborted.

Mechanics of Handling Exceptions in C#

If you've ever used exceptions in other C-style languages such as C++, Java, or even C/C++ using the Microsoft structured exception-handling extensions (__try/__catch/__finally), then you're already familiar with the basic syntax of exceptions in C#. In that case, you may find yourself skimming the next few sections or treating the material as a refresher. I've tried to point out any areas that are significantly different from the other C-style languages in the process.

Throwing Exceptions

The act of throwing an exception is actually quite easy. You simply execute a throw statement whose parameter is the exception you would like to throw. For example, suppose you have written a custom collection class that allows users to access items by index, and you would like to notify users when an invalid index is passed as a parameter. You could throw an ArgumentOutOfRange exception, as in the following code:

public class MyCollection
{
    public object GetItem( int index ) {
        if( index < 0 || index >= count ) {
            throw new ArgumentOutOfRangeException();
        }

        // Do other useful stuff here
    }

    private int count;
}

The runtime can also throw exceptions as a side effect to code execution. An example of a system-generated exception is NullReferenceException, which occurs if you attempt to access a field or call a method on an object when, in fact, the reference to the object doesn't exist.

Changes with Unhandled Exceptions Starting with .NET 2.0

When an exception is thrown, the runtime begins to search up the stack for a matching catch block for the exception. As it walks up the execution stack, it unwinds the stack at the same time, cleaning up each frame along the way.

If the search ends in the last frame for the thread, and it still finds no handler for the exception, the exception is considered unhandled at that point. What happens next depends on what version of the .NET Framework your code uses.

Note

You can install an unhandled-exception filter by registering a delegate with AppDomain.UnhandledException. When an unhandled exception comes up through the stack, this delegate will be called and will receive an instance of UnhandledExceptionEventArgs.

Note

The CLR translates unhandled exceptions passing through static constructors. I'll cover that in more detail in the section titled "Exceptions Thrown in Static Constructors."

In .NET 1.1, the CLR designers decided to swallow certain unhandled exceptions in the pursuit of greater stability. For example, if a finalizer throws an exception in .NET 1.1 instead of aborting the finalizer thread and the process, the exception is swallowed and not allowed to kill the finalizer thread or terminate the process. Similarly, if an unhandled exception percolates up in a thread other than the main thread, that thread is terminated without affecting the rest of the process. In a thread-pool thread, the exception is swallowed and the thread is returned to the pool, which is behavior that is similar to exception handling in the finalizer thread. If an unhandled exception propagates up from the main thread, then it behaves as expected, and either the process is terminated or the JIT debugging dialog is displayed, asking the user what to do.

This behavior sounds good in concept, but, in reality, it gives the opposite of the desired result. Instead of providing greater stability, systems become unstable because code runs in a nondeterministic state. For example, consider a finalizer that does some crucial work. Suppose that halfway through that work, an exception is thrown. The second half of the work in the finalizer never runs. Now, the system is in a potentially unstable, half-baked state. Everything continues to run normally, although the state of the system could be far from normal. In practice, this causes great instability because the sources of the errors are hard to find because the exceptions are swallowed. Debugging these problems is extremely difficult because the point in time where the exceptional condition occurred is long before you typically notice the resulting instability.

.NET 2.0 solves this problem by requiring that any unhandled exception, except AppDomainUnloadException and ThreadAbortException, causes the thread to terminate. It sounds rude, but, in reality, this is the behavior you should want from an unhandled exception. After all, it's an unhandled exception. Now that the thread terminates as it should, a big red flag is raised at the point of the exception that allows you to find the problem immediately and fix it. This is always a good thing. You always want errors to present themselves as soon as possible; never swallow exceptions and just let the system keep running as if everything were normal.

Note

If you really want the unhandled-exception behavior to emulate the .NET 1.1 behavior, you can request that by adding the following option to the application's configuration file.

<system>
    <runtime>
        <legacyUnhandledExceptionPolicy enabled="1"/>
    </runtime>
</system>

Syntax Overview of the try, catch, and finally Statements

The code within a try block is guarded against exceptions such that, if an exception is thrown, the runtime searches for a suitable catch block to handle the exception. Whether a suitable catch block exists or not, if a finally block is provided, the finally block is always executed no matter how execution flow leaves the try block. Let's look at an example of a C# try statement:

using System;
using System.Collections;
using System.Runtime.CompilerServices;

// Disable compiler warning: CS1058
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = false)]

public class EntryPoint
{
    static void Main() {
        try {
            ArrayList list = new ArrayList();
            list.Add( 1 );

            Console.WriteLine( "Item 10 = {0}", list[10] );
        }
        catch( ArgumentOutOfRangeException x ) {
            Console.WriteLine( "=== ArgumentOutOfRangeException"+
                               " Handler ===" );
            Console.WriteLine( x );
            Console.WriteLine( "=== ArgumentOutOfRangeException"+
                               " Handler ===

" );
        }
        catch( Exception x ) {
            Console.WriteLine( "=== Exception Handler ===" );
            Console.WriteLine( x );
            Console.WriteLine( "=== Exception Handler ===

" );
        }
        catch {
            Console.WriteLine( "=== Unexpected Exception" +
                               " Handler ===" );
            Console.WriteLine( "An exception I was not" +
                               " expecting..." );
            Console.WriteLine( "=== Unexpected Exception" +
                               " Handler ===" );
        }
        finally {
            Console.WriteLine( "Cleaning up..." );
        }
    }
}

Once you see the code in the try block, you know it is destined to throw an ArgumentOutOfRange exception. Once the exception is thrown, the runtime begins searching for a suitable catch clause that is part of this try statement and matches the type of the exception as best as possible. Clearly, the first catch clause is the one that fits best. Therefore, the runtime will immediately begin executing the statements in the first catch block. Had I not been interested in the actual exception contents, I could have left off the declaration of the exception variable x in the catch clause and only declared the type. But in this case, I wanted to demonstrate that exception objects in C# produce a nice stack trace, among other data, that can be useful during debugging. While generating the output for this chapter, I compiled the samples without debugging symbols turned on. However, if you turn on debugging symbols, you'll notice that the stack trace also includes file and line numbers of the various levels in the stack.

The second catch clause will catch exceptions of the general Exception type. Should the code in the try block throw an exception derived from System.Exception other than ArgumentOutOfRangeException, then this catch block would handle it. In C#, multiple catch clauses associated with a single try block must be ordered such that more specific exception types are listed first. The C# compiler will simply not compile code in which more general catch clauses are listed before more specific catch clauses. You can verify this by swapping the order of the first two catch clauses in the previous example in which case you would get the following compiler error:

error CS0160: A previous catch clause already catches all exceptions of this or of a super
type ('System.Exception')

In C#, every exception that you can possibly throw must derive from System.Exception. I declared a catch clause that traps exceptions of type System.Exception specifically, but what's the story with the third and last catch clause? Even though it is impossible to throw an exception of any type not derived from System.Exception in the C# language, it is not impossible in the CLR. (For example, you can throw an exception of any type in C++.) Therefore, if you wrote ArrayList in a language that allows this, it's possible that the code could throw a not-very-useful type, such as System.Int32. It sounds strange, but it is possible. In this case, you can catch such an exception in C# by using a catch clause with neither an explicit exception type nor a variable. Unfortunately, there's no easy way to know what type the thrown exception is. Also, a try statement can have, at most, one such general catch clause.

Note

Starting with .NET 2.0, the situation regarding general catch clauses is a little different than in .NET 1.1. It features a new attribute, RuntimeCompatibilityAttribute, that you can attach to your assembly. The C# and Visual Basic compilers that target .NET 2.0 apply this property by default. It tells the runtime to wrap exceptions that are not derived from System.Exception inside an exception of type RuntimeWrappedException, which is derived from System.Exception. This is handy, because it allows your C# code to access the thrown exception. Previously, you could not access the thrown exception, because it was caught by a general, parameterless catch clause. You can access the actual thrown exception type via the RuntimeWrappedException.WrappedException property. If your code contains a parameterless catch clause, the compiler emits a warning of type CS1058 by default, unless you disable the attribute as I did in the previous example.

Last of all, there is the finally block. No matter how the try block is exited, whether by reaching the end point of the block or via an exception or a return statement, the finally block will always execute. If there is a suitable catch block and a finally block associated with the same try block, the catch block executes before the finally block. You can see this by looking at the output of the previous code example, which looks like the following:

=== ArgumentOutOfRangeException Handler ===
System.ArgumentOutOfRangeException: Index was out of range.  Must be

non-negative and less than the size of the collection.

Parameter name: index

   at System.Collections.ArrayList.get_Item(Int32 index)

   at Entrypoint.Main()

=== ArgumentOutOfRangeException Handler ===
Cleaning up...

Rethrowing Exceptions and Translating Exceptions

Within a particular stack frame, you may find it necessary to catch all exceptions, or a specific subset of exceptions, long enough to do some cleanup and then rethrow the exception in order to let it continue to propagate up the stack. To do this, you use the throw statement with no parameter:

using System;
using System.Collections;

public class Entrypoint
{
    static void Main() {
        try {
            try {
                ArrayList list = new ArrayList();
                list.Add( 1 );

                Console.WriteLine( "Item 10 = {0}", list[10] );
            }
            catch( ArgumentOutOfRangeException ) {
                Console.WriteLine( "Do some useful work and" +
                                   " then rethrow" );

                // Rethrow caught exception.
                throw;
            }
            finally {
                Console.WriteLine( "Cleaning up..." );
            }
        }
catch {
            Console.WriteLine( "Done" );
        }
    }
}

Note that any finally blocks associated with the exception frame that the catch block is associated with will execute before any higher-level exception handlers are executed. You can see this in the output from the code:

Do some useful work and then rethrow
Cleaning up...
Done

In the "Achieving Exception Neutrality" section, I introduce some techniques that can help you avoid having to catch an exception, do cleanup, and then rethrow the exception. Such a work flow is cumbersome, because you must be careful to rethrow the exception appropriately. If you accidentally forget to rethrow, things could get ugly, because you would not likely be remedying the exceptional situation. The techniques that I show you will help you to achieve a goal where the only place to introduce a catch block is at the point where correctional action can occur.

Sometimes, you may find it necessary to "translate" an exception within an exception handler. In this case, you catch an exception of one type, but you throw an exception of a different, possibly more precise, type in the catch block for the next level of exception handlers to deal with. Consider the following example:

using System;
using System.Collections;

public class MyException : Exception
{
    public MyException( String reason, Exception inner )
        :base( reason, inner ) {
    }
}

public class Entrypoint
{
    static void Main() {
        try {
            try {
                ArrayList list = new ArrayList();
                list.Add( 1 );

                Console.WriteLine( "Item 10 = {0}", list[10] );
            }
            catch( ArgumentOutOfRangeException x ) {
                Console.WriteLine( "Do some useful work" +
" and then rethrow" );
                throw new MyException( "I'd rather throw this",
                                       x ) ;
            }
            finally {
                Console.WriteLine( "Cleaning up..." );
            }
        }
        catch( Exception x ) {
            Console.WriteLine( x );
            Console.WriteLine( "Done" );
        }
    }
}

One special quality of the System.Exception type is its ability to contain an inner exception reference via the Exception.InnerException property. This way, when the new exception is thrown, you can preserve the chain of exceptions for the handlers that process them. I recommend you use this useful feature of the standard exception type of C# when you translate exceptions. The output from the previous code is as follows:

Do some useful work and then rethrow
Cleaning up...

MyException: I'd rather throw this --> System.ArgumentOutOfRangeException: ~CCC

Index was out of range. ~CCC

Must be non-negative and less than the size of the collection.

Parameter name: index

   at System.Collections.ArrayList.get_Item(Int32 index)

   at Entrypoint.Main()

   -- End of inner exception stack trace --

   at Entrypoint.Main()
Done

Keep in mind that you should avoid translating exceptions if possible. The more you catch and then rethrow within a stack, the more you insulate the code that handles the exception from the code that throws the exception. That is, it's harder to correlate the point of catch to the original point of throw. Yes, the Exception.InnerException property helps mitigate some of this disconnect, but it still can be tricky to find the root cause of a problem if there are exception translations along the way.

Exceptions Thrown in finally Blocks

It is possible, but highly inadvisable, to throw exceptions within a finally block. The following code shows an example:

using System;
using System.Collections;

public class Entrypoint
{
    static void Main() {
        try {
            try {
                ArrayList list = new ArrayList();
                list.Add( 1 );

                Console.WriteLine( "Item 10 = {0}", list[10] );
            }
            finally {
                Console.WriteLine( "Cleaning up..." );
                throw new Exception( "I like to throw" );
            }
        }
        catch( ArgumentOutOfRangeException ) {
            Console.WriteLine( "Oops!  Argument out of range!" );
        }
        catch {
            Console.WriteLine( "Done" );
        }
    }
}

The first exception, ArgumentOutOfRangeException in this case, is simply lost, and the new exception is propagated up the stack. Clearly, this is not desirable. You never want to lose track of exceptions, because it becomes virtually impossible to determine what caused an exception in the first place.

Exceptions Thrown in Finalizers

C# destructors are not really deterministic destructors, but rather CLR finalizers. Finalizers are run in the context of the finalizer thread, which is effectively an arbitrary thread context. If the finalizer were to throw an exception, the CLR might not know how to handle the situation and might simply shut down the thread (and the process). Consider the following code:

using System;

public class Person
{
    ~Person() {
Console.WriteLine( "Cleaning up Person..." );
        Console.WriteLine( "Done Cleaning up Person..." );
    }
}

public class Employee : Person
{
    ~Employee() {
        Console.WriteLine( "Cleaning up Employee..." );
        object obj = null;

        // The following will throw an exception.
        Console.WriteLine( obj.ToString() );
        Console.WriteLine( "Done cleaning up Employee..." );
    }
}

public class Entrypoint
{
    static void Main() {
        Employee emp = new Employee();
        emp = null;
    }
}

The output from executing this code is as follows:

Cleaning up Employee...
Unhandled Exception: System.NullReferenceException: Object reference not set ~CCC

to an instance of an object.

   at Employee.Finalize()

Cleaning up Person...
Done Cleaning up Person...

You will notice slightly different behavior with this example between the .NET 1.1 and .NET 2.0 and later runtimes. In .NET 1.1, the exception is swallowed while it is logged in the console, and execution then continues. Starting with .NET 2.0, your development environment presents you with the familiar JIT debugger dialog, asking if you would like to debug the application. The problem is, you have a limited amount of time within which to respond before the runtime aborts your application. If you haven't already, be sure to read about how .NET 1.1 and .NET 2.0 and later treat unhandled exceptions differently in this chapter's previous section, "Changes with Unhandled Exceptions in .NET 2.0."

You should avoid knowingly throwing exceptions in finalizers at all costs, because you could abort the process. As a final note, be sure to read about all of the pros and cons of creating a finalizer in the first place in Chapter 13.

Exceptions Thrown in Static Constructors

If an exception is thrown and there is no handler in the stack, so that the search for the handler ends up in the static constructor for a type, the runtime handles this case specially. It translates the exception into a System.TypeInitializationException and throws that instead. Before throwing the new exception, it sets the InnerException property of the TypeInitializationException instance to the original exception. That way, any handler for type initialization exceptions can easily find out exactly why things failed.

Translating such an exception makes sense because constructors cannot, by their very nature, have a return value to indicate success or failure. Exceptions are the only mechanism you have to indicate that a constructor has failed. More importantly, because the system calls static constructors at system-defined times,[19] it makes sense for them to use the TypeInitializationException type in order to be more specific about when something went wrong. For example, suppose you have a static constructor that can potentially throw an ArgumentOutOfRangeException. Now, imagine the frustration users would have if your exception propagated out to the enclosing thread at some seemingly random time, because the exact moment of a static constructor call is system-defined. It could appear that the ArgumentOutOfRange exception materialized out of thin air. Wrapping your exception inside a TypeInitializationException takes a little of the mystery out of it and tips off users, or hopefully the developer, that the problem happened during type initialization.

The following code shows an example of what a TypeInitializationException with an inner exception looks like:

using System;
using System.IO;

class EventLogger
{
    static EventLogger() {
        eventLog = File.CreateText( "logfile.txt" );

        // Statement below will throw an exception.
        strLogName = (string) strLogName.Clone();
    }

    static public void WriteLog( string someText ) {
        eventLog.Write( someText );
    }

    static private StreamWriter eventLog;
    static private string       strLogName;
}
public class EntryPoint
{
    static void Main() {
        EventLogger.WriteLog( "Log this!" );
    }
}

When you run this example, the output looks like the following:

Unhandled Exception: System.TypeInitializationException:
The type initializer for 'EventLogger' threw

an exception. —-> System.NullReferenceException: Object reference not set ~CCC

to an instance of an object.

   at EventLogger..cctor()

   -- End of inner exception stack trace --
at EntryPoint.Main()

Notice that along with describing that the outermost exception is of type TypeInitializationException, the output also shows that the inner exception, which started it all, is a NullReferenceException.

Who Should Handle Exceptions?

Where should you handle exceptions? You can find the answer by applying a variant of the Expert pattern, which states that work should be done by the entity that is the expert with respect to that work. That is a circuitous way of saying that you should catch the exception at the point where you can actually handle it with some degree of knowledge available to remedy the exceptional situation. Sometimes, the catching entity could be close to the point of the exception generation within the stack frame. The code could catch the exception, then take some corrective action, and then allow the program to continue to execute normally. In other cases, the only reasonable place to catch an exception is at the entry-point Main method, at which point you could either abort the process after providing some useful data, or reset the process as if the application were just restarted. The bottom line is that you should figure out the best way to recover from exceptions, if that is possible, and the best place to do so based upon where it makes the most sense to do it. Always keep in mind that, in many cases, it is impossible to recover from an exceptional condition within the scope of the running application.

Avoid Using Exceptions to Control Flow

It can be tempting to use exceptions to manage the flow of execution in complex methods. This is never a good idea, for a couple of reasons. First, exceptions are generally expensive to generate and handle. Therefore, if you were to use them to control execution flow within a method that is at the heart of your application, your performance would likely degrade. Second, it trivializes the exceptional nature of exceptions in the first place. The whole point of exceptions is to indicate an exceptional condition in a way that can be handled or reported cleanly.

Historically, programmers have been rather lazy when it comes to handling error conditions. How many times have you seen code where the programmer never even bothered to check the return value of an API function or a method call? Such lackadaisical approaches to error handling can lead to headaches in a hurry. Exceptions provide a syntactically succinct and uniform way to indicate and handle error conditions without littering your code with a plethora of if blocks and other traditional (nonexception-based) error-handling constructs. At the same time, the runtime supports exceptions, and it does a lot of work on your behalf when exceptions are thrown. Unwinding the stack is no trivial task in itself. Lastly, the point where an exception is thrown and the point where it's handled can be disjointed and have no connection to each other. Thus, it can be difficult when reading code to determine where an exception will be caught and handled. These reasons alone are enough for you to stick to traditional techniques when managing normal execution flow.

Note

You can find an article, "The Cost of Exceptions," on Rico Mariani's blog at http://blogs.msdn.com/ricom/archive/2003/12/19/44697.aspx. Rico is an expert on performance-related issues in the CLR.

Achieving Exception Neutrality

When exceptions were first added to C++, many developers were excited to be able to throw them, catch them, and handle them. In fact, a common misconception at the time was that exception handling simply consisted of strategically placing try statements throughout the code and tossing in an occasional throw when necessary. Over time, the developer community realized that dropping try statements all over the place made their code difficult to read when, most of the time, the only thing they wanted to do was clean up gracefully when an exception was thrown and allow the exception to keep propagating up the stack. Even worse, it made the code hard to write and difficult to maintain. Code that doesn't handle exceptions but is expected to behave properly in the face of exceptions is generally called exception-neutral code.

Clearly, there had to be a better way to write exception-neutral code without having to rely on writing try statements all over the place. In fact, the only place you need a try statement is the point at which you perform any sort of system recovery or logging in response to an exception. Over time, everyone started to realize that writing try statements was, in fact, the least significant part of writing exception-safe and exception-neutral code. Generally, the only code that should catch an exception is code that knows specifically how to remedy the situation. That code could even be in the main entry point and could merely reset the system to a known start state, effectively restarting the application.

By exception-neutral code, I mean code that doesn't really have the capability to specifically handle the exception but that must be able to handle exceptional conditions gracefully. Usually, this code sits somewhere on the stack in between the code that throws the exception and the code that catches the exception, and it must not be adversely affected by the exception passing through on its way up the stack. At this point, some of you are probably starting to think about the throw statement with no parameters that allows you to catch an exception, do some work, and then rethrow the exception. However, I want to introduce you to an arguably cleaner technique that allows you to write exception-neutral code without using a single try statement and that also produces code that is easier to read and more robust.

Basic Structure of Exception-Neutral Code

The general idea behind writing exception-neutral code is similar to the idea behind creating commit/rollback code. You write such code with the guarantee that if it doesn't finish to completion, the entire operation is reverted with no change in state to the system. The changes in state are committed only if the code reaches the end of its execution path. You should code your methods like this in order for them to be exception-neutral. If an exception is thrown before the end of the method, the state of the system should remain unchanged. The following shows how you should structure your methods in order to achieve these goals:

void ExceptionNeutralMethod()
{
   //__________
   // All code that could possibly throw exceptions is in this
   // first section. In this section, no changes in state are
   // applied to any objects in the system including this.
   //__________

   //__________
   // All changes are committed at this point using operations
   // strictly guaranteed not to throw exceptions.
   //__________
}

As you can see, this technique doesn't work unless you have a set of operations that are guaranteed never to throw exceptions. Otherwise, it would be impossible to implement the commit/rollback behavior as illustrated. Thankfully, the .NET runtime does provide quite a few operations that the specification guarantees will never throw exceptions.

Let's start by building an example to describe what I mean. Suppose you have a system or an application where you're managing employees. For the sake of argument, say that once an employee is created and represented by an Employee object, it must exist within one and only one collection in the system. Currently, the only two collections in the system are one to represent active employees and one to represent terminated employees. Additionally, the collections exist within an EmployeeDatabase object, as shown in the following example:

using System.Collections;

class EmployeeDatabase
{
    private ArrayList activeEmployees;
    private ArrayList terminatedEmployees;
}

The example uses collections of the ArrayList type, which is contained in the System.Collections namespace. A real-world system would probably use something more useful, such as a database.

Now, let's see what happens when an employee quits. Naturally, you need to move that employee from the activeEmployees to the terminatedEmployees collection. A naïve attempt at such a task could look like the following:

using System.Collections;

class Employee
{
}

class EmployeeDatabase
{
    public void TerminateEmployee( int index ) {
        object employee = activeEmployees[index];
        activeEmployees.RemoveAt( index );
        terminatedEmployees.Add( employee );
    }

    private ArrayList activeEmployees;
    private ArrayList terminatedEmployees;
}

This code looks reasonable enough. The method that does the move assumes that the calling code somehow figured out the index for the current employee in the activeEmployees list prior to calling TerminateEmployee. It copies a reference to the designated employee, removes that reference from activeEmployees, and adds it to the terminatedEmployees collection. So what's so bad about this method?

Look at the method closely, and think about where exceptions could be generated. The fact is, an exception could be thrown upon execution of any of the method calls in this method. If the index is out of range, you would expect to see ArgumentOutOfRange exceptions thrown from the first two lines. Of course, if the range exception is thrown from the first line, execution would never see the second line, but you get the idea. And, if memory is scarce, it's possible that the call to Add could fail with an exception. The danger comes from the possibility of the exception being thrown after the state of the system is modified. Suppose the index passed in is valid. The first two lines will likely succeed. However, if an exception is thrown while trying to add the employee to terminatedEmployees, then the employee will get lost in the system. So, what can you do to fix the glitch?

An initial attempt could use try statements to avoid damage to the system state. Consider the following example.

using System.Collections;

class Employee
{
}

class EmployeeDatabase
{
    public void TerminateEmployee( int index ) {
        object employee = null;
        try {
            employee = activeEmployees[index];
        }
        catch {
            // oops!  We must be out of range here.
}

        if( employee != null ) {
            activeEmployees.RemoveAt( index );

            try {
                terminatedEmployees.Add( employee );
            }
            catch {
                // oops! Allocation may have failed.
                activeEmployees.Add( employee );
            }
        }
    }

    private ArrayList activeEmployees;
    private ArrayList terminatedEmployees;
}

Look how quickly the code becomes hard to read and understand, thanks to the try statements. You have to pull the employee variable declaration to outside of the try statement and initialize it to null. Once you attempt to get the reference to the employee instance, you have to check the reference for null to make sure you actually got a valid reference. Once that succeeds, you can proceed to add the employee to the terminatedEmployees list. However, if that fails for some reason, you need to put the employee back into the activeEmployees list. Also, notice that the call to RemoveAt is not inside of a try block. That's ok, because if that fails, no state has been modified at that point and we have no need to catch that.

You may have already spotted a multitude of problems with this approach. First of all, what happens if you have a failure to add the employee back into the activeEmployees collection? Do you just fail at that point? That's unacceptable, because the state of the system has changed already. Second, you probably need to return an error code from this method to indicate why it may have failed. That's something I didn't do in the previous code. The method can't just return happily as if everything went smoothly when, in fact, the action failed to complete. Third, the code is just plain ugly and hard to read. Lastly, a variety of problems still exist with this code that I won't waste time going into.

So what's the solution? Well, think of what you attempted to do with the try statements. You want to do the actions that possibly throw exceptions, and if they fail, revert to the previous state. You can actually perform a variation on this theme without try statements that goes like this: Attempt all of the actions in the method that could throw exceptions up front, and once you get past that point, commit those actions using operations that can't throw exceptions.

Note

The C++ community has accepted these techniques, thanks, in part, to the excellent works published by Herb Sutter in his Exceptional C++ series (Boston, MA: Addison-Wesley Professional). There is no good reason why you cannot apply the same techniques wholesale in the C# world.

Let's see what this method would look like:

using System.Collections;

class Employee
{
}

class EmployeeDatabase
{
    public void TerminateEmployee( int index ) {
        // Clone sensitive objects.
        ArrayList tempActiveEmployees =
            (ArrayList) activeEmployees.Clone()[20];
        ArrayList tempTerminatedEmployees =
            (ArrayList) terminatedEmployees.Clone();

        // Perform actions on temp objects.
        object employee = tempActiveEmployees[index];
        tempActiveEmployees.RemoveAt( index );
        tempTerminatedEmployees.Add( employee );

        // Now commit the changes.
        ArrayList tempSpace = null;
        ListSwap( ref activeEmployees,
                  ref tempActiveEmployees,
                  ref tempSpace );
        ListSwap( ref terminatedEmployees,
                  ref tempTerminatedEmployees,
                  ref tempSpace );
    }

    void ListSwap( ref ArrayList first,
                   ref ArrayList second,
                   ref ArrayList temp ) {
        temp = first;
        first = second;
        second = temp;
        temp = null;
    }

    private ArrayList activeEmployees;
    private ArrayList terminatedEmployees;
}

First, notice the absence of any try statements. The nice thing about their absence is that the method doesn't need to return a result code. The caller can expect the method to either work as advertised or throw an exception otherwise. The only two lines in the method that affect the state of the system are the last two calls to ListSwap. ListSwap was introduced to allow you to swap the references of the ArrayList objects in the EmployeeDatabase with the references to the temporary modified copies that you made.

How is this technique so much better when it appears to be so much less efficient? There are two tricks here. The obvious one is that, no matter where in this method an exception is thrown, the state of the EmployeeDatabase will remain unaffected. But what if an exception is thrown inside ListSwap? Ah! Here you have the second trick: ListSwap will never throw an exception. One of the most important features required in order to create exception-neutral code is that you have a small set of operations that are guaranteed not to fail under normal circumstances. No, I'm not including the case of some bozo pulling the plug on the computer in the middle of a ListSwap call, nor am I considering the case of a catastrophic earthquake or tornado at that point either. Let's see why ListSwap won't throw any exceptions.

In order to create exception-neutral code, it's imperative that you have a handful of operations, such as an assignment operation, that are guaranteed not to throw. Thankfully, the CLR provides such operations. The assignment of references, when no conversion is required, is one example of a nonthrowing operation. Every reference to an object is stored in a location, and that location has a type associated with it. However, once the locations exist, copying a reference from one to the other is a simple memory copy to already allocated locations, just like a regular pointer copy in C++, and that cannot fail. That's great for when you're copying references of one type to references of the same type.

But what happens when a conversion is necessary? Can that throw an exception? The C# standard specifies that conforming implicit conversion operators will never throw an exception. If your assignment invokes an implicit conversion, you're covered, assuming that any custom implicit conversion operators don't throw.[21] If you find a custom implicit conversion that throws an exception, I suggest you throw the C# specification at the author immediately. However, explicit conversions, in the form of casts, can throw. The bottom line is, a simple assignment from one reference to another, whether it requires implicit conversion or not, will not throw.

Simple assignment from one reference location to another is all that ListSwap is doing. After you set up the temporary ArrayList objects with the desired state, and you've gotten to the point of even executing the ListSwap calls, you've arrived at a point where you know that no more exceptions in the TerminateEmployee method are possible. Now you can make the switch safely. The ArrayList objects in the EmployeeDatabase object are swapped with the temporary ones. Once the method completes, the original ArrayList objects are free to be collected by the GC.

One more thing that you may have noticed regarding ListSwap is that the temporary location to store an ArrayList instance during the swap is allocated outside the ListSwap method and passed in as a ref parameter. I did this in order to avoid a StackOverflowException inside ListSwap. It's remotely possible that, when calling ListSwap, the stack could be running on vapors, and the mere allocation of another stack slot could fail and generate an exception. Thus, I performed that step outside of the confines of the ListSwap method. Once execution is inside ListSwap, all the locations are allocated and ready for use.

This technique, when applied liberally in a system that requires rigid stability, will quickly point out methods that may be too complex and need to be broken up into smaller functional units. In essence, this idiom amplifies the complexity of a particular method it is applied to. Therefore, if you find that it becomes unwieldy and difficult to make the method bulletproof, you should analyze the method and make sure it's not trying to do too much work that you could break up into smaller units.

Incidentally, you may find it necessary to make swap operations, similar to ListSwap, atomic in a multithreaded environment. You could modify ListSwap to use some sort of exclusive lock object, such as a mutex or a System.Threading.Monitor object. However, you may find yourself inadvertently making ListSwap capable of throwing exceptions, and that violates the requirements on ListSwap. Thankfully, the System.Threading namespace offers the Interlocked class to perform these swap operations atomically, and best of all, the methods are guaranteed never to throw exceptions. The Interlocked class provides a generic overload of all of the useful methods, making them very efficient. The generic Interlocked methods come with a constraint that they only work with reference types. Chapter 12 contains more information on the use of the Interlocked class.

The bottom line is, you should do everything that can possibly throw an exception before modifying the state of the object being operated on. Once you know you're past the point of possibly causing any exceptions, commit the changes using operations that are guaranteed not to throw exceptions.

If you're tasked to create a robust, real-world system where many people rely on the integrity of the system, I cannot stress how much this idiom is a must. Sure, it's not as efficient at runtime as the naïve approach, and it requires more system resources to succeed effectively, but your clients will prefer the inefficiency over corrupt data any day. Your colleagues will thank you, too, because resource leaks and other related glitches caused as a side effect of a thrown exception are tricky to find due to their out-of-band nature.

Constrained Execution Regions

The example in the previous section demonstrates some of the level of paranoia you must endure in order to write bulletproof, exception-neutral code. I was so paranoid that a stack overflow would occur that I allocated the extra space needed by ListSwap before I called the method. You would think that would take care of all of the issues. Unfortunately, you'd be wrong. In the CLR environment, other asynchronous exceptions could occur, such as a ThreadAbortException (which I cover in Chapter 12) and OutOfMemoryException and StackOverflowException exceptions.

For example, what if, during the commit phase of the TerminateEmployee method, the application domain is shut down, forcing a ThreadAbortException? Or what if, during the first call to ListSwap, the JIT compiler fails to allocate enough memory to compile the method in the first place? Clearly, these bad situations are difficult to deal with. In fact, in the .NET 1.1 days, you couldn't do much to handle these diabolical situations. However, starting in .NET 2.0, you can use a constrained execution region (CER) or a critical finalizer.

A CER is a region of code that the CLR prepares prior to executing, so that when the code is needed, everything is in place and the failure possibilities are mitigated. Moreover, the CLR postpones the delivery of any asynchronous exceptions, such as ThreadAbortException exceptions, if the code in the CER is executing. You can perform the magic of CERs using the RuntimeHelpers class in the System.Runtime.CompilerServices namespace. To create a CER, simply call RuntimeHelpers.PrepareConstrainedRegions prior to a try statement in your code. The CLR then examines the catch and finally blocks and prepares them by walking through the call graph and making sure all methods in the execution path are JIT-compiled and sufficient stack space is available.[22] Even though you call PrepareConstrainedRegions prior to a try statement, the actual code within the try block is not prepared. Therefore, you can use the following idiom for preparing arbitrary sections of code by wrapping the code in a finally block within a CER:

RuntimeHelpers.PrepareConstrainedRegions();
try {} finally
{
// critical code goes here
}

Let's look at how you can modify the previous example using a CER to make it even more reliable:

using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;

class Employee
{
}

class EmployeeDatabase
{
    public void TerminateEmployee( int index ) {
        // Clone sensitive objects.
        ArrayList tempActiveEmployees =
            (ArrayList) activeEmployees.Clone();
        ArrayList tempTerminatedEmployees =
            (ArrayList) terminatedEmployees.Clone();

        // Perform actions on temp objects.
        object employee = tempActiveEmployees[index];
        tempActiveEmployees.RemoveAt( index );
        tempTerminatedEmployees.Add( employee );

        RuntimeHelpers.PrepareConstrainedRegions();
        try {} finally {
            // Now commit the changes.
            ArrayList tempSpace = null;
            ListSwap( ref activeEmployees,
                      ref tempActiveEmployees,
                      ref tempSpace );
            ListSwap( ref terminatedEmployees,
                      ref tempTerminatedEmployees,
                      ref tempSpace );
        }
    }

    [ReliabilityContract( Consistency.WillNotCorruptState,
                          Cer.Success )]
    void ListSwap( ref ArrayList first,
                   ref ArrayList second,
                   ref ArrayList temp ) {
        temp = first;
        first = second;
        second = temp;
        temp = null;
    }

    private ArrayList activeEmployees;
    private ArrayList terminatedEmployees;
}

Notice that the commit section of the TerminateEmployee method is wrapped inside a CER. At runtime, prior to executing that code, the CLR prepares the code by also preparing the ListSwap method and ensuring that the stack can handle the work. Of course, this preparation operation may fail, and that's OK, because you're not yet into the code that commits the changes. Notice the addition of the ReliabilityContractAttribute to the ListSwap method. This informs the runtime of what sorts of guarantees the ListSwap method adheres to so that the CER can be formed properly. You could also attach a ReliabilityContractAttribute to the TerminateEmployee method, but it really is only useful for code executed inside a CER. If you want to attach this attribute to the TerminateEmployee method so that you can use it in a CER created elsewhere, you could add the following attribute:

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]

This ReliabilityContractAttribute expresses the goal that you set out to achieve with TerminateEmployee in the first place. Because the Consistency is set to Consistency.WillNotCorruptState, then even though the method may fail, if it does, the state of the system won't be corrupted. Other values you could provide are Consistency.MayCorruptProcess, Consistency.MayCorruptAppDomain, and Consistency.MayCorruptInstance. Their names are self descriptive of what they mean and you can read more about these in the MSDN documentation, but for the most robust software, it should be obvious that you will want your methods to adhere to the Consistency.WillNotCorruptState reliability contract.

Note

Even though the CLR guarantees that asynchronous exceptions won't be injected into the thread while inside a CER, it doesn't provide any guarantee about suppressing all exceptions. It only suppresses the ones that are outside of your control. That means that if you create objects within a CER, you must be prepared to deal with OutOfMemoryException or any other such code-induced exception.

Critical Finalizers and SafeHandle

Critical finalizers are similar to CERs, in that the code within them is protected from asynchronous exceptions and other such dangers caused by running in a virtual execution system that are outside your control. To mark your object as having a critical finalizer, simply derive from CriticalFinalizerObject. By doing so, you guarantee your object to have a finalizer that runs within the context of a CER, and therefore, must abide by all of the rules imposed by a CER. Additionally, the CLR will execute critical finalizers after it finishes dealing with all other noncritical finalizable objects.

In reality, it's rare that you'll ever need to create a critical finalizable object. Critical finalizable objects are used most commonly in interop code. You can usually get the behavior you need by deriving from SafeHandle because often you end up holding on to native handles in interop code. SafeHandle is a critical tool when creating native interop code through P/Invoke or COM interop, because it allows you to guarantee that you won't leak any unmanaged resources from within the CLR. Prior to .NET 2.0, this was not possible. In the .NET 1.1 days, you would typically represent an opaque native handle type with a managed IntPtr type. This works just fine, except that you cannot guarantee that the underlying resource will be cleaned up in the event of an asynchronous exception such as a ThreadAbortException. As usual, by adding an extra level of indirection[23] in the form of a SafeHandle, you can mitigate these problems in .NET 2.0.

Warning

Before you jump to the conclusion that you need to create a SafeHandle derivative, be sure to check if one of the supplied SafeHandle derivatives in the .NET Framework will work for you. For example, if you're creating code to talk directly to a device driver by calling the Win32 DeviceIoControl function via P/Invoke, then the SafeFileHandle type is sufficient for holding the handle that you open directly on the driver.

When creating your own SafeHandle derived class, you must follow a short list of steps. As an example, let's create a SafeHandle derived class, SafeBluetoothRadioFindHandle, to enumerate through the Bluetooth radios on a system, assuming there are any. The pattern for enumerating Bluetooth radios in native code is quite simple and a common theme used throughout the Win32 API. You call the Win32 BluetoothFindFirstRadio function, and if it succeeds, it returns the first radio handle through an out parameter and an enumeration handle through the return value. You can find any additional radios by calling the Win32 function BluetoothFindNextRadio while passing the enumeration handle obtained from BluetoothFindFirstRadio. When finished, you must be sure to call the Win32 function BluetoothFindRadioClose on the enumeration handle. Consider the following code:

using System;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Microsoft.Win32.SafeHandles;


//
// Matches Win32 BLUETOOTH_FIND_RADIO_PARAMS
//
[StructLayout( LayoutKind.Sequential )]
class BluetoothFindRadioParams
{
    public BluetoothFindRadioParams() {
        dwSize = 4;
    }
    public UInt32 dwSize;
}

//
// Matches Win32 BLUETOOTH_RADIO_INFO
//
[StructLayout( LayoutKind.Sequential,
               CharSet = CharSet.Unicode )]
struct BluetoothRadioInfo
{
    public const int BLUETOOTH_MAX_NAME_SIZE = 248;

    public UInt32 dwSize;
    public UInt64 address;
    [MarshalAs( UnmanagedType.ByValTStr,
                SizeConst = BLUETOOTH_MAX_NAME_SIZE )]
    public string szName;
    public UInt32 ulClassOfDevice;
    public UInt16 lmpSubversion;
    public UInt16 manufacturer;
}

//
// Safe Bluetooth Enumeration Handle
//
[SecurityPermission( SecurityAction.Demand,
                     UnmanagedCode = true )]
sealed public class SafeBluetoothRadioFindHandle
    : SafeHandleZeroOrMinusOneIsInvalid
{
    private SafeBluetoothRadioFindHandle() : base( true ) { }

    override protected bool ReleaseHandle() {
        return BluetoothFindRadioClose( handle );
    }

    [DllImport( "Irprops.cpl" )]
    [ReliabilityContract( Consistency.WillNotCorruptState,
                          Cer.Success )]
    [SuppressUnmanagedCodeSecurity]
    private static extern bool BluetoothFindRadioClose(
                                              IntPtr hFind );
}

public class EntryPoint
{
    private const int ERROR_SUCCESS = 0;

    static void Main() {
        SafeFileHandle radioHandle;
        using( SafeBluetoothRadioFindHandle radioFindHandle
            = BluetoothFindFirstRadio(new BluetoothFindRadioParams(),
                                      out radioHandle) ) {
            if( !radioFindHandle.IsInvalid ) {
                BluetoothRadioInfo radioInfo = new BluetoothRadioInfo();
                radioInfo.dwSize = 520;
                UInt32 result = BluetoothGetRadioInfo( radioHandle,
                                                       ref radioInfo );
if( result == ERROR_SUCCESS ) {
                    // Let's send the contents of the radio info to the
                    // console.
                    Console.WriteLine( "address = {0:X}",
                                       radioInfo.address );
                    Console.WriteLine( "szName = {0}",
                                       radioInfo.szName );
                    Console.WriteLine( "ulClassOfDevice = {0}",
                                       radioInfo.ulClassOfDevice );
                    Console.WriteLine( "lmpSubversion = {0}",
                                       radioInfo.lmpSubversion );
                    Console.WriteLine( "manufacturer = {0}",
                                       radioInfo.manufacturer );
                }

                radioHandle.Dispose();
            }
        }
    }

    [DllImport( "Irprops.cpl" )]
    private static extern SafeBluetoothRadioFindHandle
        BluetoothFindFirstRadio( [MarshalAs(UnmanagedType.LPStruct)]
                                 BluetoothFindRadioParams pbtfrp,
                                 out SafeFileHandle phRadio );

    [DllImport( "Irprops.cpl" )]
    private static extern UInt32
        BluetoothGetRadioInfo( SafeFileHandle hRadio,
                               ref BluetoothRadioInfo pRadioInfo );
}

The crux of this example is SafeBluetoothRadioFindHandle. You could have derived it directly from SafeHandle, but the runtime provides two helper classes, SafeHandleZeroOrMinusOneIsInvalid and SafeHandleMinusOneIsInvalid, to derive from in order to make things easier.

Warning

Be careful when dealing with Win32 functions via P/Invoke, and always read the documentation carefully to see what constitutes an invalid handle value. The Win32 API is notorious for making this confusing. For example, the Win32 CreateFile function returns −1 to represent a failure. The CreateEvent function returns a NULL handle in the event of an error. In both cases, the return type is HANDLE.

Take several things into consideration when providing your own SafeHandle derivative:

  • Apply a code access security demand on the class requiring the ability to call unmanaged code: Of course, you don't need to do this unless you really do call unmanaged code, but the odds of your ever creating a SafeHandle derivative and not calling unmanaged code are very slim.

  • Provide a default constructor that initializes the SafeHandle derivative: Notice that SafeBluetoothRadioFindHandle declares a private default constructor. The P/Invoke layer possesses special powers, thus it can create instances of the object even though the constructor is private. The private constructor keeps clients from creating instances without calling the Win32 functions that create the underlying resource.

  • Override the virtual IsInvalid property: In this case, that was not necessary because the base class SafeHandleZeroOrMinusOneIsInvalid handles that for you.

  • Override the virtual ReleaseHandle method, which is used to clean up the resource: Typically, this is where you'll make your call through P/Invoke to release the unmanaged resource. In the example, you make a call to BluetoothFindRadioClose. Note that when declaring the method for P/Invoke, you apply a reliability contract, because the ReleaseHandle method is called within the context of a CER. Additionally, it's wise to apply the SuppressUnmanagedCodeSecurityAttribute to the method to prevent the Code Access Security (CAS) feature of the CLR from walking the stack checking security each time it is called. Keep in mind that doing so implies that your code is secure.

Once you define your SafeHandle derivative, you're ready to use it in your P/Invoke declarations. In the preceding example, you declare the BluetoothFindFirstRadio method to be called through P/Invoke. If you look up this function in the Microsoft Developer Network (MSDN), you'll see that it returns a BLUETOOTH_RADIO_FIND type, which is a handle to an internal radio enumeration object. In the .NET 1.1 days, you would have declared the method as returning an IntPtr. Starting with .NET 2.0, you indicate that it returns a SafeBluetoothRadioFindHandle type, and the P/Invoke marshaling layer handles the rest. Now, the enumeration handle is safe from being leaked by the runtime in the event of some asynchronous exception introduced by the virtual execution system.

Warning

When marshaling between a COM method or Win32 function that returns a handle contained within a structure (as opposed to simply returning a handle), the interop layer doesn't provide support for dealing with SafeHandle derivatives. In these rare cases, you'll need to call SetHandle on the SafeHandle derivative after getting the structure back from the function or COM method so that the SafeHandle instance can manage the lifetime of the unmanaged resource.. However, if you have to do such a thing, you want to make sure that the operation that creates the handle and the subsequent SetHandle method call occurs within a CER so that nothing can interrupt the process of allocating the resource and assigning the handle to the SafeHandle object; otherwise, your application could leak resources.

Creating Custom Exception Classes

System.Exception has three public constructors and one protected constructor. The first is the default constructor, which doesn't really do much of anything. The second is a constructor that accepts a reference to a string object. This string is a general, programmer-defined message that you can consider a more user-friendly description of the exception. The third is a constructor that takes a message string, like the second constructor, but it also accepts a reference to another Exception. The reference to the other exception allows you to keep track of originating exceptions when one exception is translated into another exception within a try block. A good example of that is when an exception is not handled and percolates up to the stack frame of a static constructor. In that case, the runtime throws a TypeInitializationException, but only after it sets the inner exception to that of the original exception so that the one who catches the TypeInitializationException will at least know why this exception occurred in the first place. Finally, the protected constructor allows creation of an exception from a SerializationInfo object. You always want to create serializable exceptions so you can use them across context boundaries—for example, with .NET Remoting. That means you'll also want to mark your custom exception classes with the SerializableAttribute as well.

The System.Exception class is very useful with these three public constructors. However, it would constitute a bad design to simply throw objects of type System.Exception any time anything goes wrong. Instead, it would make much more sense to create a new, more specific, exception type that derives from System.Exception. That way, the type of the exception is much more expressive about the problem at hand. Even better than that is the fact that your derived exception class could contain data that is germane to the reason the exception was thrown in the first place. And remember, in C#, all exceptions must derive from System.Exception. Let's see what it takes to define custom exceptions effectively.

Consider the previous EmployeeDatabase example. Suppose that in order to add an employee to the database, the employee's data must be validated. If an employee's data does not validate, the Add method will throw an exception of type EmployeeVerificationException. Notice that I'm ending the new exception's type name with the word Exception. This is a good habit to get into, and a recommended convention, because it makes it easy to spot exception types within your type system. It's also considered good style within the C# programming community. Let's see what such an exception type could look like:

using System;
using System.Runtime.Serialization;

[Serializable()]
public class EmployeeVerificationException : Exception
{
    public enum Cause {
        InvalidSSN,
        InvalidBirthDate
    }

    public EmployeeVerificationException( Cause reason )
        :base() {
        this.Reason = reason;
    }

    public EmployeeVerificationException( Cause reason,
                                          String msg )
        :base( msg ) {
        this.Reason = reason;
    }
public EmployeeVerificationException( Cause reason,
                                          String msg,
                                          Exception inner )
        :base( msg, inner ) {
        this.Reason = reason;
    }

    protected EmployeeVerificationException(
                   SerializationInfo info,
                   StreamingContext context )
                :base( info, context ) { }

    public Cause Reason { get; private set; }
}

In the EmployeeDatabase.Add method, you can see the simple call to Validate on the emp object. This is a rather crude example, where you force the validation to fail by throwing an EmployeeVerificationException. But the main focus of the example is the creation of the new exception type. Many times, you'll find that just creating a new exception type is good enough to convey the extra information you need to convey. In this case, I wanted to illustrate an example where the exception type carries more information about the validation failure, so I created a Reason property whose backing field must be initialized in the constructor. Also, notice that EmployeeVerificationException derives from System.Exception. At one point, the school of thought was that all .NET Framework-defined exception types would derive from System.Exception, while all user-defined exceptions would derive from ApplicationException, thus making it easier to tell the two apart. This goal has been lost partly due to the fact that some .NET Framework-defined exception types derive from ApplicationException.[24]

You may be wondering why I defined four exception constructors for this simple exception type. The traditional idiom when defining new exception types is to define the same four public constructors that System.Exception exposes. Had I decided not to carry the extra reason data, then the EmployeeVerificationException constructors would have matched the System.Exception constructors exactly in their form. If you follow this idiom when defining your own exception types, users will be able to treat your new exception type in the same way as other system-defined exceptions. Plus, your derived exception will be able to leverage the message and inner exception already encapsulated by System.Exception.

Working with Allocated Resources and Exceptions

If you're a seasoned C++ pro, then one thing you have most definitely been grappling with in the C# world is the lack of deterministic destruction. C++ developers have become accustomed to using constructors and destructors of stack-based objects to manage precious resources. This idiom even has a name: Resource Acquisition Is Initialization (RAII). This means that you can create objects on the C++ stack where some precious resource is allocated in the constructor of those objects, and if you put the deallocation in the destructor, you can rely upon the destructor getting called at the proper time to clean up. For example, no matter how the stack-based object goes out of scope—whether it's through normal execution while reaching the end of the scope or via an exception—you can always be guaranteed that the destructor will execute, thus cleaning up the precious resource.

When C# and the CLR were first introduced to developers during the beta program, many developers immediately became very vocal about this omission in the runtime. Whether you view it as an omission or not, it clearly was not addressed to its fullest extent until after the beta developer community applied a gentle nudge. The problem stems, in part, from the garbage-collected nature of objects in the CLR, coupled with the fact that the friendly destructor in the C# syntax was reused to implement object finalizers. It's also important to remember that finalizers are very different from destructors. Using the destructor syntax for finalizers only added to the confusion of the matter. There were also other technical reasons, some dealing with efficiency, why deterministic destructors as we know them were not included in the runtime.

After knocking heads for some time, the solution put on the table was the Disposable pattern that you utilize by implementing the IDisposable interface. For more detailed discussions relating to the Disposable pattern and your objects, refer to Chapter 4 and Chapter 13. Essentially, if your object needs deterministic destruction, it obtains it by implementing the IDisposable interface. However, you have to call your Dispose method explicitly in order to clean up after the disposable object. If you forget to, and your object is coded properly, then the resource won't be lost—rather, it will just be cleaned up when the GC finally gets around to calling your finalizer. Within C++, you only have to remember to put your cleanup code in the destructor, and you never have to remember to clean up after your local objects, because cleanup happens automatically once they go out of scope.

Consider the following contrived example that illustrates the danger you can face:

using System;
using System.IO;
using System.Text;

public class EntryPoint
{
    public static void DoSomeStuff() {
        // Open a file.
        FileStream fs = File.Open( "log.txt",
                                   FileMode.Append,
                                   FileAccess.Write,
                                   FileShare.None );
        Byte[] msg = new UTF8Encoding(true).GetBytes("Doing Some"+
                                                     " Stuff");
        fs.Write( msg, 0, msg.Length );
    }

    public static void DoSomeMoreStuff() {
        // Open a file.
        FileStream fs = File.Open( "log.txt",
                                   FileMode.Append,
                                   FileAccess.Write,
                                   FileShare.None );
        Byte[] msg = new UTF8Encoding(true).GetBytes("Doing Some"+
                                                     " More Stuff");
        fs.Write( msg, 0, msg.Length );
    }

    static void Main() {
        DoSomeStuff();
DoSomeMoreStuff();
    }
}

This code looks innocent enough. However, if you execute this code, you'll most likely encounter an IOException. The code in DoSomeStuff creates a FileStream object with an exclusive lock on the file. Once the FileStream object goes out of scope at the end of the function, it is marked for collection, but you're at the mercy of the GC and when it decides to do the cleanup. Therefore, when you find yourself opening the file again in DoSomeMoreStuff, you'll get the exception, because the precious resource is still locked by the unreachable FileStream object. Clearly, this is a horrible position to be in. And don't even think about making an explicit call to GC.Collect in Main before the call to DoSomeMoreStuff. Fiddling with the GC algorithm by forcing it to collect at specific times is a recipe for poor performance. You cannot possibly help the GC do its job better, because you have no specific idea how it is implemented.

So what is one to do? One way or another, you must ensure that the file gets closed. However, here's the rub: No matter how you do it, you must remember to do it. This is in contrast to C++, where you can put the cleanup in the destructor and then just rest assured that the resource will get cleaned up in a timely manner. One option would be to call the Close method on the FileStream in each of the methods that use it. That works fine, but it's much less automatic and something you must always remember to do. However, even if you do, what happens if an exception is thrown before the Close method is called? You find yourself back in the same boat as before, with a resource dangling out there that you can't get to in order to free it.

Those who are savvy with exception handling will notice that you can solve the problem using some try/finally blocks, as in the following example:

using System;
using System.IO;
using System.Text;

public class EntryPoint
{
    public static void DoSomeStuff() {
        // Open a file.
        FileStream fs = null;
        try {
            fs = File.Open( "log.txt",
                            FileMode.Append,
                            FileAccess.Write,
                            FileShare.None );
            Byte[] msg =
                new UTF8Encoding(true).GetBytes("Doing Some"+
                                                " Stuff
");
            fs.Write( msg, 0, msg.Length );
        }
        finally {
            if( fs != null ) {
                fs.Close();
            }
        }
    }

    public static void DoSomeMoreStuff() {
        // Open a file.
FileStream fs = null;
        try {
            fs = File.Open( "log.txt",
                            FileMode.Append,
                            FileAccess.Write,
                            FileShare.None );
            Byte[] msg =
                new UTF8Encoding(true).GetBytes("Doing Some"+
                                                " More Stuff
");
            fs.Write( msg, 0, msg.Length );
        }
        finally {
            if( fs != null ) {
                fs.Close();
            }
        }
    }

    static void Main() {
        DoSomeStuff();

        DoSomeMoreStuff();
    }
}

The try/finally blocks solve the problem. But, yikes! Notice how ugly the code just got. Plus, let's face it, many of us are lazy typists, and that was a lot of extra typing. Moreover, more typing means more places for bugs to be introduced. Lastly, it makes the code difficult to read. As you'd expect, there is a better way. Many objects, such as FileStream, that have a Close method also implement the IDisposable pattern. Usually, calling Dispose on these objects is the same as calling Close. Of course, calling Close over Dispose or vice versa is arguing over apples and oranges, if you still have to explicitly call one or the other. Thankfully, there's a good reason why most classes that have a Close method implement Dispose—so you can use them effectively with the using statement, which is typically used as part of the Disposable pattern in C#. Therefore, you could change the code to the following:

using System;
using System.IO;
using System.Text;

public class EntryPoint
{
    public static void DoSomeStuff() {
        // Open a file.
        using( FileStream fs = File.Open( "log.txt",
                                          FileMode.Append,
                                          FileAccess.Write,
                                          FileShare.None ) ) {
            Byte[] msg =
                new UTF8Encoding(true).GetBytes("Doing Some" +
                                                " Stuff
");
            fs.Write( msg, 0, msg.Length );
        }
    }
public static void DoSomeMoreStuff() {
        // Open a file.
        using( FileStream fs = File.Open( "log.txt",
                                          FileMode.Append,
                                          FileAccess.Write,
                                          FileShare.None ) ) {
            Byte[] msg =
                new UTF8Encoding(true).GetBytes("Doing Some" +
                                                " More Stuff
");
            fs.Write( msg, 0, msg.Length );
        }
    }

    static void Main() {
        DoSomeStuff();

        DoSomeMoreStuff();
    }
}

As you can see, the code is much easier to follow, and the using statement takes care of having to type all those explicit try/finally blocks. You probably won't be surprised to notice that if you look at the generated code in ILDASM, the compiler has generated the try/finally blocks in place of the using statement. You can also nest using statements within their compound blocks, just as you can nest try/finally blocks.

Even though the using statement solves the "ugly code" symptom and reduces the chances of typing in extra bugs, it still requires that you remember to use it in the first place. It's not as convenient as the deterministic destruction of local objects in C++, but it's better than littering your code with try/finally blocks all over the place, and it's definitely better than nothing. The end result is that C# does have a form of deterministic destruction via the using statement, but it's only deterministic if you remember to make it deterministic.

Providing Rollback Behavior

When producing exception-neutral methods, as covered in the "Achieving Exception Neutrality" section of this chapter, you'll often find it handy to employ a mechanism that can roll back any changes if an exception happens to be generated. You can solve this problem by using the classic technique of introducing one more level of indirection in the form of a helper class. For the sake of discussion, let's use an object that represents a database connection, and that has methods named Commit and Rollback.

In the C++ world, a popular solution to this problem involves the creation of a helper class that is created on the stack. The helper class also has a method named Commit. When called, it just passes through to the database object's method, but before doing so, it sets an internal flag. The trick is in the destructor. If the destructor executes before the flag is set, there are only a couple of ways that is possible. First, the user might have forgotten to call Commit. That's a bug in the code, so let's not consider that option. The second way to get into the destructor without the flag set is if the object is being cleaned up because the stack is unwinding as it looks for a handler for a thrown exception. Depending on the state of the flag in the destructor code, you can instantly tell if you got here via normal execution or via an exception. If you got here via an exception, all you have to do is call Rollback on the database object, and you have the functionality you need.

Now, this is all great in the land of native C++, where you can use deterministic destruction. However, you can get the same end result using the C# form of deterministic destruction, which is the marriage between IDisposable and the using keyword. Remember, a destructor in native C++ maps into an implementation of the IDisposable interface in C#. All you have to do is take the code that you would have put into the destructor in C++ into the Dispose method of the C# helper class. Let's take a look at what this C# helper class could look like:

using System;
using System.Diagnostics;

public class Database
{
    public void Commit() {
        Console.WriteLine( "Changes Committed" );
    }

    public void Rollback() {
        Console.WriteLine( "Changes Abandoned" );
    }
}

public class RollbackHelper : IDisposable
{
    public RollbackHelper( Database db ) {
        this.db = db;
    }

    ~RollbackHelper() {
        Dispose( false );
    }

    public void Dispose() {
        Dispose( true );
    }

    public void Commit() {
        db.Commit();
        committed = true;
    }

    private void Dispose( bool disposing ) {
        // Don't do anything if already disposed.  Remember, it is
        // valid to call Dispose() multiple times on a disposable
        // object.
        if( !disposed ) {
            disposed = true;

            // Remember, we don't want to do anything to the db if
            // we got here from the finalizer, because the database
            // field could already be finalized!
            if( disposing ) {
                if( !committed ) {
                    db.Rollback();
                }
} else {
                Debug.Assert( false, "Failed to call Dispose()" +
                                     " on RollbackHelper" );
            }
        }
    }

    private Database db;
    private bool     disposed = false;
    private bool     committed = false;
}

public class EntryPoint
{
    static private void DoSomeWork() {
        using( RollbackHelper guard = new RollbackHelper(db) ) {
            // Here we do some work that could throw an exception.

            // Comment out the following line to cause an
            // exception.
            // nullPtr.GetType();

            // If we get here, we commit.
            guard.Commit();
        }

    }

    static void Main() {
        db = new Database();
        DoSomeWork();
    }

    static private Database db;
    static private Object   nullPtr = null;
}

Inside the DoSomeWork method is where you'll do some work that could fail with an exception. Should an exception occur, you'll want any changes that have gone into the Database object to be reverted. Inside the using block, you've created a new RollbackHelper object that contains a reference to the Database object. If control flow gets to the point of calling Commit on the guard reference, all is well, assuming the Commit method does not throw. Even if it does throw, you should code it in such a way that the Database remains in a valid state. However, if your code inside the guarded block throws an exception, the Dispose method in the RollbackHelper will diligently roll back your database.

No matter what happens, the Dispose method will be called on the RollbackHelper instance, thanks to the using block. If you forget the using block, the finalizer for the RollbackHelper will not be able to do anything for you, because finalization of objects goes in random order, and the Database referenced by the RollbackHelper could be finalized prior to the RollbackHelper instance. To help you find the places where you brain-froze, you can code an assertion into the helper object as I have previously done. The whole use of this pattern hinges on the using block, so, for the sake of the remaining discussion, let's assume you didn't forget it.

Once execution is safely inside the Dispose method, and it got there via a call to Dispose rather than through the finalizer, it simply checks the committed flag, and if it's not set, it calls Rollback on the Database instance. That's all there is to it. It's almost as elegant as the C++ solution except that, as in previous discussions in this chapter, you must remember to use the using keyword to make it work. If you'd like to see what happens in a case where an exception is thrown, simply uncomment the attempt to access the null reference inside the DoSomeWork method.

You may have noticed that I haven't addressed what happens if Rollback throws an exception. Clearly, for robust code, it's optimal to require that whatever operations RollbackHelper performs in the process of a rollback should be guaranteed never to throw. This goes back to one of the most basic requirements for generating strong exception-safe and exception-neutral code: In order to create robust exception-safe code, you must have a well-defined set of operations that are guaranteed not to throw. In the C++ world, during the stack unwind caused by an exception, the rollback happens within a destructor. Seasoned C++ salts know that you should never throw an exception in a destructor, because if the stack is in the process of unwinding during an exception when that happens, your process is aborted very rudely. And there's nothing worse than an application disappearing out from under users without a trace. But what happens if such a thing happens in C#? Remember, a using block is expanded into a try/finally block under the covers. And you may recall that when an exception is thrown within a finally block that is executing as the result of a previous exception, that previous exception is simply lost and the new exception gets thrown. What's worse is that the finally block that was executing never gets to finish. That, coupled with the fact that losing exception information is always bad and makes it terribly difficult to find problems, means that it is strongly recommended that you never throw an exception inside a finally block. I know I've mentioned this before in this chapter, but it's so important it deserves a second mention. The CLR won't abort your application, but your application will likely be in an undefined state if an exception is thrown during execution of a finally block, and you'll be left wondering how it got into such an ugly state.

Summary

In this chapter, I covered the basics of exception handling along with how you should apply the Expert pattern to determine the best place to handle a particular exception. I touched upon the differences between .NET 1.1 and later versions of the CLR when handling unhandled exceptions and how .NET 2.0 and later respond in a more consistent manner. The meat of this chapter described techniques for creating bulletproof exception-safe code that guarantees system stability in the face of unexpected exceptional events. I also described constrained execution regions that you can use to postpone asynchronous exceptions during thread termination. Creating bulletproof exception-safe and exception-neutral code is no easy task. Unfortunately, the huge majority of software systems in existence today flat-out ignore the problem altogether. It's an extremely unfortunate situation, given the wealth of resources that have become available ever since exception handling was added to the C++ language years ago.

Sadly, for many developers, exception safety is an afterthought. They erroneously assume they can solve any exceptional problems during testing by sprinkling try statements throughout their code. In reality, exception safety is a crucial issue that you should consider at software design time. Failure to do so will result in substandard systems that will do nothing but frustrate users and lose market share to those companies whose developers spent a little extra time getting exception safety right. Moreover, there's always the possibility, as computers integrate more and more into people's daily lives, that government regulations could force systems to undergo rigorous testing in order to prove they are worthy for society to rely upon. Don't think you may be the exception, either (no pun intended). I can envision an environment where a socialist government could force such rules on any commercially sold software (shudder). Have you ever heard stories about how, for example, the entire integrated air traffic control system in a country or continent went down because of a software glitch? Wouldn't you hate to be the developer who skimped on exception safety and caused such a situation? I rest my case.

In the next chapter, I'll cover the main facets of dealing with strings in C# and the .NET Framework. Additionally, I'll cover the important topic of globalization.



[18] If you're not familiar with the term stack frame, you may want to reference http://en.wikipedia.org/wiki/Stack_frame. In short, as each method is called throughout the execution of a program, a frame is built on the stack that contains the passed parameters and any local parameters to the method. The frame is deleted upon return from the method. However, as the method calls other methods, and so on, new frames are stacked on top of the current frame, thus implementing a nested call-frame structure.

[19] The system could call static constructors at type load time or just prior to a static member access, depending on how the CLR is configured for the current process.

[20] Be sure to read the section in Chapter 13 regarding ICloneable and how you probably should avoid it in the first place.

[21] The C# reference explicitly states that custom implicit conversion operators must not throw exceptions.

[22] Incidentally, virtual methods and delegates pose a problem, because the call graph is not deducible at preparation time. However, if you know the target of the virtual method or delegate, you can prepare it explicitly by calling RuntimeHelpers.PrepareDelegate. I recommend reading Stephen Toub's Keep Your Code Running with the Reliability Features of the .NET Framework, available at http://msdn.microsoft.com/msdnmag/issues/05/10/Reliability/default.aspx.

[23] Andrew Koenig of C++ fame likes to call this the Fundamental Theorem of Software Engineering—that is, that any software engineering problem can be solved by adding a level of indirection.

[24] For more on this subject and many other useful guidelines, reference Krzysztof Cwalina and Brad Abrams' Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition) (Boston, MA: Addison-Wesley Professional, 2008).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.56.12