Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#
Article

Simplifying Exception-Safe Code

Rate me:
Please Sign up or sign in to vote.
4.67/5 (21 votes)
1 Apr 20039 min read 104.4K   36   22
Exception correctness can be more easily attainted with the help of a couple of utility classes and C#'s "using" clause.

Introduction

Writing correct code in the presence of exceptions is no easy task. Exceptions introduce a control flow that is very different from normal program flow. And it takes some forethought and careful coding to guarantee the consistency and predictability of a program in the (not so) unlikely event of throwing exceptions.

It is no wonder then that most developers write their code with the total disregard to exception safety. We always assume that we can "fix it" later on, which most often ends up to be in a bug report from a very unhappy customer using the released product.

But What is Exception Safety?

In his remarkable book--Exceptional C++--Herb Sutter defines the various levels of exception safety. While his treatment is focused on C++, I find his definitions applicable to C#, and indeed to any language that supports the notion of exceptions. According to his definition, there are three levels of exception safety:

  • Strong Guarantee, in which case an operation failing because of an exception will not alter the program state. This implies that such operation is treated as an atomic unit, which implies some form of commit-or-rollback semantics. That basically means that client code calling a function will perceive no side effects if that function throws an exception.
  • Basic Guarantee, which only guarantees that throwing an exception will not leak any resources, but side effects may arise still. The effect of leaking resources isn't a big issue due to garbage collection of managed resources. But it can be a real problem when dealing with limited resources such as database connections, sockets, file handles, and the likes.
  • No-throw Guarantee, in which case a method guarantees that it will handle all thrown exceptions locally and it will not raise them to the caller, at the same time assuring the consistency of the program's state. This type of exception safety is necessary in certain cases in C++ code, but I haven't found any compelling reason to use it in C#. Thus the focus of this article will be on the strong exception safety guarantee.

In both the strong and the basic guarantee, the developer might choose to wrap thrown exceptions in application-specific ones before propagating them to the caller. But note that the .Net documentation suggests that it is usually better to propagate built-in exceptions directly to the caller without wrapping them.

Exceptions In Action: An Example

Let's assume that you are developing an instant messaging application. Each user is represented by a User class. Each user has a list of bodies that are stored both locally on the client and on the messaging server. The server database is represented by a ServerDB class that acts as a facade, hiding the gory details of updating the server.

The code for the ServerDB class might look like the following:

C#
class ServerDB
{
    .
    .
    .
    public static void AddBuddy( User theUser, User buddy )
    {
        .
        .
        .
    }
    .
    .
    .
}

While the User class might look like the following

C#
public class User
{
    // The SetCollection is a hypothetical container resembling
    // the System.Collections.Dictionary class, except that it
    // only stores keys, but not values. Similar to std::set
    // in C++
    private SetCollection m_buddies;
    .
    .
    .
    public void AddBuddy( User buddy )
    {
        m_buddies.Add( User );
        ServerDB.AddBuddy( this, buddy );
    }
}

SetCollection.Add might throw an exception if we attempt to add a buddy that already existed in the collections, or it might throw an exception if it was unable to allocate enough memory (unlikely in a garbage-collected environment, yet still a possibility.) ServerDB.AddBuddy might throw an exception for several reasons, such as not being able to connect to the remote server. Although this seems to be a very remote possibility, do keep in mind that this is meant to be a trivial example. The example might be more realistic if we assume that SetCollection would attempt to persist its state to an external file. In such a case, the probability of SetCollection.Add throwing an exception would increase drastically.

The code snippet above upholds the strong exception safety guarantee if SetCollection.Add throws. But if the ServerDB.AddBuddy throws, the in-memory copy of the buddies list would be inconsistent with the server.

A better attempt might be something like this:

C#
public void AddBuddy( User buddy )
{
    m_buddies.Add( buddy );
    try
    {
        ServerDB.AddBuddy( this, buddy )
    }
    catch( Exception ex )
    {
        m_buddies.Remove( buddy );
        throw ex;
    }
}

This works fine. The code upholds the strong exception safety guarantee at the cost of increased code size and, most importantly, reduced readability. The purpose of the code is obscured a little by the exception handling code. I trust that you could imagine the situation if the method was more complex than this trivial two-liner.

A Better Solution: The using Statement

As you recall from your favorite C# textbook, the using keyword performs two very different functions based on its context: it is used to import namespaces into the current compilation unit, and it is also used in conjunction with the IDisposable interface to guarantee proper clean up of resources at the end of a code block.

For example, suppose that we have a class ResourceWrapper that implements IDisposable and wraps a limited resource that must be closed as soon as possible. Client code using such a class might look like the following (the example is taken directly from the MSDN library):

C#
class ResourceWrapper
    : IDisposable
{
    .
    .
    .

    public void Dispose()
    {
        // clean up code
    }
}

class myApp
{
   public static void Main()
   {
      using (ResourceWrapper r1 = new ResourceWrapper())
      {
         .
         .
         // some code
         .
         .

         r1.DoSomething();
      }
   }
}

No matter how the code exits the using block, whether through an exception or a normal return, ResourceWrapper.Dispose will always get called. Such behavior upholds the strong exception gaurantee quite nicely, and it can be a good starting point to a solution to our problem.

Back to our original example, the using directive and the IDisposable interface combo don't work well for our situation. Remember that we want some sort of clean up action to take place only in the presence of exceptions. Also keep in mind that it's unlikely that we would be able to change the definition of SetCollection to support IDisposable, since doing so would drastically alter the usage semantics of the class. We could, however, implement a helper class to do the clean up on behalf of SetCollection. We'll call this class BuddyInserter, and we'll define it as follows:

C#
public class BuddyInserter
    : IDisposable
{
    private bool            m_disposed = false;
    private bool            m_dismissed = false;
    private User            m_insertedBuddy = null;
    private SetCollection    m_theCollection = null;

    public BuddyInserter( SetCollection theCollection, User buddy )
    {
        m_insertedBuddy = buddy;
        m_theCollection = theCollection;
    }

    ~BuddyInserter()
    {
        Dispose( false );
    }

    public void Dispose()
    {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    protected void Dispose( bool disposing )
    {
        // Make sure that we are not disposed already
        if ( m_disposed == false )
        {
            // If we are NOT being called from the finalizer,
            // then we should release all resources, managed
            // and unmanaged. But we are not holding any
            // unmanaged resources
            if ( disposing )
            {
                // Check to see if rollback is needed
                if ( m_dismissed == false )
                {
                    try
                    {
                        m_theCollection.Remove( m_insertedBuddy );
                    }
                    catch
                    {
                        // Do nothing
                    }
                }
            }

            m_disposed = true;
        }
    }

    // Supress rollback action
    public void Dismiss()
    {
        m_dismissed = true;
    }
}

You might notice that the implementation of the protected Dispose method is a little verbose. But it follows the guidelines of the MSDN documentation to the letter. The BuddyInserter class holds the information necessary for it to rollback the operation if and only if the Dismiss method doesn't get called before Dispose. In our example, we would use this class in the following way:

C#
public class User
{
    .
    .
    .
    public void AddBuddy( User buddy )
    {
        m_buddies.Add( buddy );
        using ( BuddyInserter inserter =<BR>		new BuddyInserter( m_buddies, buddy ) )
        {
            ServerDB.AddBuddy( this, buddy );
            inserter.Dimiss();
        }
    }
}

Now the code above is both simpler and cleaner than the previous attempt. If an exception is thrown from SetCollection.Add, the state remains consistent and the exception propagates to the caller without any side effects. If an exception is thrown from ServerDB.AddBuddy, the Dispose method of BuddyInserter will rollback the collection to a consistent state with the server database.

But still, the above solution is a minor improvement in terms of readability, and hand crafting a class like BuddyInserter each and every time your run into a similar situation is a daunting task that will lead to more code bloat. It's obvious that this solution is not practical, so the ever pressured programmer is likely to revert back to the following solution:

C#
public void AddBuddy( User buddy )
{
    m_buddies.Add( User );
    ServerDB.AddBuddy( this, buddy );
}

And we are back to square one again. But there's still hope yet.

A Practical Solution

Using reflection and two helper classes, we can attain a solution that is both elegant and practical. We'll start by defining a simple collection of IDisposable objects:

C#
public class ScopeGuard
    : IDisposable
{
    private bool m_disposed = false;
    private Stack m_disposables = new Stack();

    public ScopeGuard()
    {
    }

    ~ScopeGuard()
    {
        this.Dispose( false );
    }

    // Begin IDisposable implementation
    public void Dispose()
    {
        this.Dispose( true );
        GC.SuppressFinalize( this );
    }

    protected void Dispose( bool disposing )
    {
        if ( m_disposed == false )
        {
            if ( disposing == true )
            {
                // do managed resources clean up here
                while ( m_disposables.Count != 0 )
                {
                    IDisposable disposableObject
                        = (IDisposable) m_disposables.Pop();

                    disposableObject.Dispose();
                }
            }
            m_disposed = true;
        }
    }
    // End IDisposable implementation


    public void Add( IDisposable disposableObject )
    {
        Debug.Assert( disposableObject != null );

        m_disposables.Push( disposableObject );
    }
}

The ScopeGuard has only one method of its own; Add, which takes an object implementing IDisposable and stores it into a stack collection. The Dispose method simply pops the IDisposable objects off the stack and calls their corresponding Dispose methods. The use of a stack to store the objects is crucial here, since we must guarantee that objects are disposed in reverse order of their addition.

Using this class with the BuddyInserter class that we defined earlier greatly enhances the readability of the code:

C#
public void AddBuddy( User buddy )
{
    using ( ScopeGuard guard = new ScopeGuard() )
    {
        m_buddies.Add( buddy );
        BuddyInserter inserter = new BuddyInserter( m_buddies, buddy );
        guard.Add( inserter );
        ServerDB.AddBuddy( this, buddy );
        inserter.Dimiss();
    }
}

In the above code, exiting the using block will call ScopeGuard.Dispose, which in turn will call BodyInseter.Dispose. BodyInseter.Dispose will either undo the insertion of the buddy or do nothing at all, depending on whether BuddyInserter.Dismiss was called or not.

But still, we would have to write our own BuddyInserter and such classes whenever we find ourselves in need of commit-or-rollback behavior. It would be nice if we could somehow write a generic object that would be able of storing a pointer to a method, an object on which to call that method, and a list of arguments, and have the object exhibit the same semantics of the BuddyInserter class above.

Delegates may come to mind as a possible solution. But you'll soon find them of little use, since you can't guarantee that the clean up methods you might need to call will always have the same signature. After some research, the only practical solution that I could find relies on reflection.

We'll need one more helper class: ObjectGuard:

C#
public class ObjectGuard
    : IDisposable
{
    private MethodInfo    m_methodInfo = null;
    private object        m_target = null;
    private object[]    m_methodArgs = null;
    private bool        m_disposed = false;
    private bool        m_dismissed = true;

    public static ObjectGuard Make( object obj, string methodName,<BR>        object[] methodArgs )
    {
        Debug.Assert( obj != null );
        Debug.Assert( methodArgs != null );

        Type objType = obj.GetType();

        Type[] argsTypes = new Type[ methodArgs.Length ];

        for ( int i = 0; i < methodArgs.Length; ++i )
        {
            argsTypes[i] = methodArgs[i].GetType();
        }

        MethodInfo methodInfo = objType.GetMethod( methodName,<BR>        argsTypes );

        Debug.Assert( methodInfo != null );

        return new ObjectGuard( obj, methodInfo, methodArgs );
    }


    private ObjectGuard( object target, MethodInfo methodInfo,<BR>        object[] methodArgs )
    {
        m_target        = target;
        m_methodInfo    = methodInfo;
        m_methodArgs    = methodArgs;
        m_dismissed        = false;
    }

    ~ObjectGuard()
    {
        Dispose( false );
    }

    public void Dispose()
    {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    protected void Dispose( bool disposing )
    {
        if ( m_disposed == false )
        {
            if ( disposing )
            {
                if ( m_dismissed == false )
                {
                    try
                    {
                        m_methodInfo.Invoke( m_target, m_methodArgs );
                    }
                    catch
                    {
                        // Do nothing
                    }
                }
            }

            m_disposed = true;
        }
    }

    public void Dismiss()
    {
        m_dismissed = true;
    }

}

Note the static factory method ObjectGuard.Make, which takes a name of the method as a string, an object to invoke the method on, and a list of arguments matching the method's signature. This method asserts that all parameters are correct and then constructs an instance of ObjectGuard class, which in implements IDisposable and follows the same semantics of the BuddyInserter class the we have seen earlier. Using this class and ScopeGuard, our AddBuddy example becomes something like the following:

C#
public void AddBuddy( User buddy )
{
    using ( ScopeGuard guard = new ScopeGuard() )
    {
        m_buddies.Add( buddy );
        ObjectGuard inserterGuard
            = ObjectGuard.Make( m_buddies, "Remove",<BR>          new object[] { buddy } );
        guard.Add( inserterGuard );
        ServerDB.AddBuddy( this, buddy );
        inserterGuard.Dimiss();
    }
}

Save using a string to pass the method name, I believe that the above code is more elegant and more practical than the usual try, catch and finally triad. Of course, there will be a performance hit due to the use of reflection. But I believe that when faced with a trade off between performance and exception safety, one should always favor the latter. Once you have a stable and correct application, let profiling guide you to any performance bottlenecks, and then optimize. Remember that (premature) optimization is evil.

Another Example: Migrating Your Old Exception Handling Code

You must have written something resembling the following code snippet at one time or another:

C#
public void InsertBuddy( User theUser, User buddy )
{
    SqlConnection conn = null;
    SqlTransaction trx = null;

    try
    {
        conn = new SqlConnection( ... );
        conn.Open();
        trx = conn.BeginTransaction();

        // do some DB manipulation

        trx.Commit();
    }
    catch( Exception ex )
    {
        trx.Rollback();
        throw ex;
    }
    finally
    {
        conn.Close();
    }
}

For the trained eye, this code is exception-safe. But the intent is obscured a little by the exception handling code, and the magic of the finally block looks--to me at least--very intimidating.

Writing the above code snippet using ScopeGuard and ObjectGuard looks a lot better in terms of both readability and maintainability:

C#
public void InsertBuddy( User theUser, User buddy )
{
    using ( ScopeGuard guard = new ScopeGuard() )
    {
        SqlConnection conn = new SqlConnection( ... );
        conn.Open();

        guard.Add( ObjectGuard.Make( conn, "Close", new object[] {} );

        SqlTransaction trx = conn.BeginTransaction();
        ObjectGuard trxGuard
            = ObjectGuard.Make( trx, "Rollback", new object[] {} );

        guard.Add( trxGuard );

        // do some DB manipulation

        trx.Commit();
        trxGuard.Dismiss();
    }
}

Please note that we didn't hold onto a reference to the ObjectGuard calling the SqlConnection.Close method, since we want the clean up action to be performed regardless of how we exit the code block.

Conclusion

It is my belief that exception safety is often a neglected topic among programmers. And my own personal experience has taught me that such negligence always comes back to bite you someday. But using the solution outlined above, I'm finding myself doing the right thing and totally enjoying it. I hope you'll give this approach a try. Your comments and suggestions are greatly appreciated.

Acknowledgments

The idea of this article came from an article by Andrei Alexandrescu and Petru Marginean published on the CUJ's experts' forum. The method that the aforementioned authors present, while done in C++, is far more superior and elegant than anything I might attempt to write.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Saudi Arabia Saudi Arabia
Mohammad Abdulfatah is currently working as a senior software developer in a small software shop where he spends most of his time maintaining tons of C++/Win32 code and writing .Net web applications. He hates having his picture taken for the same reason he freaks out when he hears his own recorded voice: they both don't look/sound like his own self-image/voice. When he is not coding for a living he could be found stuck to his computer at home coding for fun, reading a book, writing on his weblog (http://mohammad.abdulfatah.net/), or pursuing other vanity projects.

Comments and Discussions

 
SuggestionThe implementation is not "generic" Pin
DennisJonz26-Nov-19 9:56
DennisJonz26-Nov-19 9:56 
Generalguard.Add(trxGuard) missing Pin
Anonymous18-Mar-03 20:44
Anonymous18-Mar-03 20:44 
GeneralRe: guard.Add(trxGuard) missing Pin
MohammadAbdulfatah18-Mar-03 21:47
MohammadAbdulfatah18-Mar-03 21:47 
Generalrethrowing an exception Pin
Maximilian Hänel17-Mar-03 22:33
Maximilian Hänel17-Mar-03 22:33 
Hi,

first of all thanks for this great article!

In your code snippets you often write code like that
catch( Exception ex )
{
   //...
   throw ex;
}

wich actually throws a new exception with the same exception object. The downside is that you are loosing the point where the exception originally has been thrown (-> stack trace). A better approach would be to write a simple throw statement without the ex reference specified:
catch( Exception ex )
{
   //...
   throw; //
}

This rethrows the exception and preserves the stack trace of the exception.

cu

Max


"You can always improve your chances of writing
bug-free code by writing code that doesn't do anything"

Rob Macdonald, Serious ADO

GeneralRe: rethrowing an exception Pin
MohammadAbdulfatah18-Mar-03 9:32
MohammadAbdulfatah18-Mar-03 9:32 
GeneralRe: rethrowing an exception Pin
Jim Stone19-Mar-03 6:23
Jim Stone19-Mar-03 6:23 
GeneralRe: rethrowing an exception Pin
MohammadAbdulfatah19-Mar-03 7:24
MohammadAbdulfatah19-Mar-03 7:24 
GeneralRe: rethrowing an exception Pin
Brian Nottingham2-Apr-03 19:13
Brian Nottingham2-Apr-03 19:13 
GeneralRe: rethrowing an exception Pin
MohammadAbdulfatah6-Apr-03 23:04
MohammadAbdulfatah6-Apr-03 23:04 
GeneralSome corrections Pin
leppie14-Mar-03 7:42
leppie14-Mar-03 7:42 
GeneralRe: Some corrections Pin
MohammadAbdulfatah14-Mar-03 8:18
MohammadAbdulfatah14-Mar-03 8:18 
GeneralRe: Some corrections Pin
MohammadAbdulfatah19-Mar-03 7:29
MohammadAbdulfatah19-Mar-03 7:29 
GeneralRe: Some corrections Pin
leppie19-Mar-03 9:14
leppie19-Mar-03 9:14 
GeneralRe: Some corrections Pin
MohammadAbdulfatah19-Mar-03 10:40
MohammadAbdulfatah19-Mar-03 10:40 
GeneralOverhead Pin
Deyan Petrov13-Mar-03 21:32
Deyan Petrov13-Mar-03 21:32 
GeneralRe: Overhead Pin
MohammadAbdulfatah14-Mar-03 1:48
MohammadAbdulfatah14-Mar-03 1:48 
GeneralRe: Overhead Pin
Peter Gummer25-Mar-03 15:27
Peter Gummer25-Mar-03 15:27 
GeneralRe: Overhead Pin
MohammadAbdulfatah25-Mar-03 19:35
MohammadAbdulfatah25-Mar-03 19:35 
GeneralRe: Overhead Pin
Andy Smith2-Apr-03 9:42
Andy Smith2-Apr-03 9:42 
GeneralRe: Overhead Pin
MohammadAbdulfatah3-Apr-03 12:15
MohammadAbdulfatah3-Apr-03 12:15 
GeneralRe: Overhead Pin
Mark Smithson6-Apr-03 9:45
Mark Smithson6-Apr-03 9:45 
GeneralRe: Overhead Pin
MohammadAbdulfatah16-Apr-03 3:27
MohammadAbdulfatah16-Apr-03 3:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.