How to implement catch (Exception e)?

How often did you see a C# catch(Exception e) statement in code? How often did you write it yourself?

I know I do it, even when I know I shouldn’t. Why?

Because its so easy! Doing it right is hard(er) or at least take much more code you have to repeat over and over again.

But its not something that you’d be proud of (I’m not).

So, I thought it was time to change that. But how? I definitely don’t want to rewrite a lot of code for each try-catch-finally block.

First lets take a look at error handling. When do you really handle an error? Almost never I dare to say. I’ve only encountered one occasion where I really handled an exception (a dead-lock exception from SQL-Server: I waited a random amount of time a retried – three times. After that I just let the exception bubble up).

What does your error handling code look like? I bet it looks something like this:

    try
    {
        // real code here…
    }
    catch(Exception e)
    {
        Logger.LogException(e);
    }

I don’t see the handling part in this 😉 why do we call this error handling? BTW don’t call throw e in the catch block. I rewrites the call stack and you loose the original call stack.

But there’s a whole range of exceptions you don’t want to catch. AccessViolationException? ExecutionEngineException?

Those indicate situations you can’t fix anyway.

How about InvalidCastException and NullReferenceException?

Those exceptions indicate some technical error and are an indication of plain bugs. I wouldn’t want to catch those in my code (only at AppDomain level to log them).

The good news is that the BCL team is doing something about this in .NET 4.0. But even in .NET 4.0 catch(Exception e) is still not a good idea.

But how do we handle exceptions the easy way (the catch(Exception) way) but filter on the really important exceptions? We can take the solution of the BCL team one step further.

The following code is not production code but it demonstrates an idea to handle exceptions correctly once and for all.

    public class ErrorHandler
    {
        public delegate void TryCallback();
        public delegate void ExceptionCallback(Exception e);
        public delegate void FinallyCallback(bool? exception);

        public ErrorHandler()
        {
            // add "really" fatal exceptions by default.
            FatalExceptions.Add(typeof(AccessViolationException));
            FatalExceptions.Add(typeof(ExecutionEngineException));
        }

        private List<Type> _fatalExceptions = new List<Type>();
        public IList<Type> FatalExceptions
        {
            get { return _fatalExceptions; }
        }

        public bool IsFatalException(Type exceptionType)
        {
            if (!typeof(Exception).IsAssignableFrom(exceptionType))
            {
                throw new ArgumentException("Specified type is not (derived from) System.Exception.", "exceptionType");
            }

            return (_fatalExceptions.FindIndex(e => e.GetType() == exceptionType) != -1);
        }

        public bool? TryCatchFinally(TryCallback @try, ExceptionCallback @catch, FinallyCallback @finally)
        {
            bool? handleException = null;

            if (@try == null)
            {
                throw new ArgumentNullException("@try");
            }

            try
            {
                @try();
            }
            catch (Exception e)
            {
                handleException = HandleException(ref e);

                if (@catch != null && !IsFatalException(e.GetType()))
                {
                    @catch(e);
                }

                if (handleException != null)
                {
                    if (handleException == true)
                    {
                        throw e;
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            finally
            {
                if (@finally != null)
                {
                    @finally(handleException);
                }
            }

            return handleException;
        }

        public bool? HandleException(ref Exception e)
        {
            bool? result = null;

            if (e != null)
            {
                if (IsFatalException(e.GetType()))
                {
                    // throw
                    result = false;
                }
                else
                {
                    // TODO: call EntLib exception policy

                    result = false; // for now
                }
            }

            return result;
        }
    }

The HandleException method is where it gets decided whether an exception is handled and how. This is also the place to integrate EntLib if you desire. The return value of the HandleException can be null (do nothing), false (call throw) or true – meaning the exception has been replaced (exception wrapping) and throw e should be called. You could elaborate the catch callback to include retries of the @try code when you actually handle an exception (like the dead lock example earlier).

You could use this code as follows:

    public void MethodThatCouldGoWrong(string someParameter)
    {
        ErrorHandler errorHandler = new ErrorHandler();
        errorHandler.FatalExceptions.Add(typeof(InvalidCastException));
        errorHandler.FatalExceptions.Add(typeof(NullReferenceException));

        errorHandler.TryCatchFinally(
            delegate()  // try
            {
                // do something here that causes an exception
            },
            delegate(Exception e) // catch
            {
                // handle the exception e
            },
            null    // finally
            );
    }

This code will not call the catch callback on AccessViolationException, ExecutionEngineException, InvalidCastException and NullReferenceException.

You probably don’t want to instantiate the ErrorHandler class each time you need it – you could make it static as long as you add all fatal exception during initialization of that static instance. Then its a matter of calling the TryCatchFinally method and doing your processing using anonymous delegates (I think in this case its more readable than lambdas). You can even pass null to the @catch callback if you don’t have any custom handling to perform but still get your exceptions ‘handled’.

So its a start. Maybe not perfect.

Thoughts?