#912 – Intellisense Can Show Exceptions that a Method Might Throw

In some cases, when the Intellisense function within Visual Studio shows you information about a method that you are going to call, it will also list potential exceptions that the method might throw.

In the example below, we see that the Matrix.Invert method can throw an exception of type InvalidOperationException.

912-001

 

It’s not the case that every single method that you might call in the Base Class Library (BCL) will provide this information.  Some methods will list the exceptions that they might throw, but many will not.

#911 – finally Block Execution When Exception Is Rethrown

Recall that if an exception is caught in a catch block, an associated finally block will execute after the execution of the catch block.

If the catch block rethrows the exception, the finally block will execute after the catch block, but before the exception propagates back up the call stack.

In the example below, the sequence is:

  • Exception thrown from try block in HelperMethod
  • Exception caught in catch block in HelperMethodcatch block executes up to throw
  • finally block in HelperMethod executes
  • Exception caught in catch block of Main method
        static void Main(string[] args)
        {
            try
            {
                HelperMethod();
            }
            catch
            {
                Console.WriteLine("Caught exception in Main()");
            }

            Console.ReadLine();
        }

        static void HelperMethod()
        {
            try
            {
                throw new ApplicationException("Uh-oh");
            }
            catch
            {
                Console.WriteLine("In catch block, before re-throwing");
                throw;
            }
            finally
            {
                Console.WriteLine("In finally block");
            }
        }

911-001

#910 – One Example of a finally Block

You use a finally block when you have logic that should execute whether or not an exception occurs when executing a block of code.

One example of something that you might do in a finally block is to call a Dispose method on an object that implements IDisposable.  You would do this because you want to make sure that you always call Dispose on the disposable object, whether or not an exception occurs when using the object.

(Note: You can achieve the same pattern with the using statement, which automates invocation of a Dispose method).

            StreamWriter sw = new StreamWriter(@"D:\Log.txt");
            try
            {
                sw.Write(string.Format("Did something at {0}", DateTime.Now));
            }
            finally
            {
                sw.Dispose();
            }

#909 – When a finally Block Executes

The purpose of a finally block is to define some code that should always execute, whether or not an exception occurs while executing code in the corresponding try block.

Exactly when a finally block executes depends on several things:

  • If the code in the try block executes without throwing an exception or transferring control elsewhere
    • The finally block executes after the code in the try block
  • If an exception occurs while executing code in the try block ..
    • .. and the exception is caught in a catch block associated with the same try statement — the body of the catch block executes, followed by the body of the finally block
    • .. and the exception is not caught — the body of the finally block executes before the exception propagates back up the call stack
  • If a goto or return statement is encountered in the try block
    • The finally block executes before control is transferred

#908 – Handling Unhandled Exceptions

An unhandled exception is one that propagates up the call stack without being caught by an exception handler in a catch block.  By default, the .NET runtime will cause a dialog to be displayed when an unhandled exception occurs.

908-001

When an unhandled exception occurs, you can’t recover from the exception.  But you can do some final logging and then terminate the application quietly by adding a handler to the AppDomain.UnhandledException event.

        static void Main(string[] args)
        {
            // Specify handler for all unhandled exceptions
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

            throw new ApplicationException("Something bad happened");

            Console.ReadLine();
        }

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Exception theException = (Exception)e.ExceptionObject;

            Console.WriteLine("- In the unhandled exception handler -");
            Console.WriteLine(theException.ToString());

            // Exit to avoid unhandled exception dialog
            Environment.Exit(-1);
        }

908-002

#907 – Exceptions Thrown from Main Are Treated as Unhandled Exceptions

When an exception is thrown from a Main method and you don’t catch the exception within the Main method, the exception will be treated as an unhandled exception.  Because the Main method is the entry point, or topmost method, of your application, there is no higher level method where the exception can be handled.

When the unhandled exception occurs, the application will stop executing and display an error message.

In the example below, we throw an exception of type ApplicationException from the Main method, but do not catch the exception in Main.  The exception is dumped to the console output and we see an error dialog.

        static void Main(string[] args)
        {
            if (DateTime.Now.DayOfWeek == DayOfWeek.Monday)
                throw new ApplicationException("Sorry, I don't work on Mondays");

            Console.WriteLine("I'm running normally..");
        }

907-001

#906 – Adding Custom Data vs. Using a Custom Exception Type

There are two basic approaches to adding custom data to an exception that you’re going to throw.  In both cases, you can pass data back to the calling method that is unique to the specific problem that has occurred.

You can use either method, depending on the knowledge that your calling code has regarding either your custom exception type or the custom data that you add to the exception.  Creating a custom exception type gives you some more flexibility, because calling code can then add a handler that is specific to the new type.

#905 – Examining an Exception’s Stack Trace

When you access an exception object’s StackTrace property from within an exception handler, you only have access to a single multiline string that dumps out details about the stack.

For example:

905-001

This format is not ideal, since it’s fairly verbose and you have no ability to look at individual items within the stack.

If you want more control in formatting output that shows the stack, or you want to programmatically examine the stack, you can create a new System.Diagnostics.StackTrace object, passing it the Exception object that you’re handling.

Below is an example that dumps out a more abbreviated version of the stack and changes the order to match the calling sequence.

                catch (Exception ex)
                {
                    Console.WriteLine("** Caught exception, stack :");
                    StackTrace stack = new StackTrace(ex);
                    StackFrame[] frames = stack.GetFrames();
                    for (int i = frames.Count() - 1; i >= 0; i--)
                    {
                        MethodBase mb = frames[i].GetMethod();
                        Console.WriteLine("- {0}  [{1}]",
                            mb.Name,
                            mb.DeclaringType.FullName);
                    }
                }

905-002

#904 – Getting the Innermost Wrapped Exception

When you re-throw an exception, you can create a new exception that wraps the original exception by setting the InnerException property of the new exception to refer to the original exception.

An exception that wraps an inner exception can itself by wrapped.  When you catch an exception, the original cause of the exception might be several levels deep.  You can get to the original exception by looking at the InnerException property of each exception, working your way down until you find one that has a null value for InnerException.

You can also get access to the innermost exception using the Exception.GetBaseException method.  This method just follows the chain of exceptions using the InnerException and stops when it gets to the innermost exception.

                try
                {
                    DoDogStuff();
                }
                catch (ApplicationException wrappedEx)
                {
                    Console.WriteLine("** Main() caught ApplicationException:");
                    Console.WriteLine("  Base exception: ");
                    Console.WriteLine("  {0}", wrappedEx.GetBaseException().ToString());
                }

904-001

#903 – Wrapped Exceptions Can Be Several Levels Deep

When handling an exception, you can wrap the original exception in a new exception that you throw, by setting the InnerException property of the new exception to reference the original exception.

If it makes sense for your application, you can wrap an already-wrapped exception, such that the inner exception itself contains an inner exception.  You might also encounter wrapped exceptions that are several levels deep when using the .NET Framework.

In the example below, a Main method calls a DoDogStuff method.  DoDogStuff in turn calls Dog.Bark, which calls Dog.ReadBarkLog.  The original exception is wrapped, as follows:

  • ReadBarkLog throws an IOException
  • Dog.Bark catches the IOException and wraps it in a DogBarkException
  • DoDogStuff catches the DogBarkException and wraps it in an ApplicationException
  • Main catches the ApplicationException and then has access to both inner exceptions

903-001