Section 11.4. Rethrowing Exceptions


11.4. Rethrowing Exceptions

You might want your catch block to take some initial corrective action and then rethrow the exception to an outer TRy block (in a calling function). It might rethrow the same exception, or it might throw a different one. If it throws a different one, it may want to embed the original exception inside the new one so that the calling method can understand the exception history. The InnerException property of the new exception retrieves the original exception.

Some exceptions make any sense only in the context in which they were thrown. This is particularly the case with, for example, the NullReferenceException, which may result from bad user input. In cases where you can't anticipate this by checking input in advance, you should catch the exception, and rethrow an ArgumentException to provide the caller with a better indication of the cause of the problem.

It can sometimes be a good idea to put a catch handler at the boundary of a component or design layer, to trap unexpected exceptions. In this case you might throw a custom InternalErrorException signaling the client code that something went wrong within your component.


Because the InnerException is also an exception, it too might have an inner exception. Thus, an entire chain of exceptions can be nested one within the other, much like Matryoshka dolls[2] are contained one within the other. Example 11-8 illustrates.

[2] In earlier editions I referred to nested Ukrainian dolls. Many readers have written to say that they are Russian dolls. The dolls I refer to are properly called Matryoshka dolls and, according to Internet sources, are associated with both Russia and the Ukraine. As an interesting additional note, there is evidence of nested dolls as far back as 11th century China.

Example 11-8. Rethrowing inner exceptions
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace RethrowingExceptions {    public class MyCustomException : System.ApplicationException    {       public MyCustomException(          string message, Exception inner ):          base(message,inner)       {       }    }    public class Test    {       public static void Main( )       {          Test t = new Test( );          t.TestFunc( );       }       public void TestFunc( )       {          try          {             DangerousFunc1( );          }          // if you catch a custom exception             // print the exception history          catch ( MyCustomException e )          {             Console.WriteLine( "\n{0}", e.Message );             Console.WriteLine(                "Retrieving exception history..." );             Exception inner =                e.InnerException;             while ( inner != null )             {                Console.WriteLine(                   "{0}", inner.Message );                inner =                   inner.InnerException;             }          }       }       public void DangerousFunc1( )       {          try          {             DangerousFunc2( );          }          // if you catch any exception here             // throw a custom exception          catch ( System.Exception e )          {             MyCustomException ex =                new MyCustomException(                   "E3 - Custom Exception Situation!", e );             throw ex;          }       }       public void DangerousFunc2( )       {          try          {             DangerousFunc3( );          }          // if you catch a DivideByZeroException take some              // corrective action and then throw a general exception          catch ( System.DivideByZeroException e )          {             Exception ex =                new Exception(                   "E2 - Func2 caught divide by zero", e );             throw ex;          }       }       public void DangerousFunc3( )       {          try          {             DangerousFunc4( );          }          catch ( System.ArithmeticException )          {            Console.WriteLine("Arithmetic exception caught in DF3,                       and rethrown...");                throw;          }          catch ( System.Exception )          {             Console.WriteLine(                "Exception handled here." );          }       }       public void DangerousFunc4( )       {          throw new DivideByZeroException( "E1 - DivideByZero Exception" );       }    } } Output: E3 - Custom Exception Situation! Retrieving exception history... E2 - Func2 caught divide by zero E1 - DivideByZeroException

Because this code has been stripped to the essentials, the output might leave you scratching your head. The best way to see how this code works is to use the debugger to step through it.

Begin by calling DangerousFunc1() in a try block:

try {    DangerousFunc1( ); }

DangerousFunc1( ) calls DangerousFunc2(), which calls DangerousFunc3( ), which in turn calls DangerousFunc4(). All these calls are in their own TRy blocks. At the end, DangerousFunc4() throws a DivideByZeroException. System.DivideByZeroException normally has its own error message, but you are free to pass in a custom message. Here, to make it easier to identify the sequence of events, the custom message E1 - DivideByZeroException is passed in.

The exception thrown in DangerousFunc4() is caught in the catch block in DangerousFunc3(). The logic in DangerousFunc3( ) is that if any ArithmeticException is caught (such as DivideByZeroException), it takes no action; it just rethrows the exception:

catch (System.ArithmeticException) {       Console.WriteLine("Arithmetic exception caught in DF3,            and rethrown...");    throw; }

The syntax to rethrow the exact same exception (without modifying it) is just the word throw.

The exception is thus rethrown to DangerousFunc2(), which catches it, takes some corrective action, and throws a new exception of type Exception. In the constructor to that new exception, DangerousFunc2() passes in a custom message (E2 - Func2 caught divide by zero) and the original exception. Thus, the original exception (E1) becomes the InnerException for the new exception (E2). DangerousFunc2( ) then throws this new E2 exception to DangerousFunc1( ).

DangerousFunc1() catches the exception, does some work, and creates a new exception of type MyCustomException. It passes a new string (E3 - Custom Exception Situation!) to the constructor as well as the exception it just caught (E2). Remember, the exception it just caught is the exception with a DivideByZeroException (E1) as its inner exception. At this point, you have an exception of type MyCustomException (E3), with an inner exception of type Exception (E2), which in turn has an inner exception of type DivideByZeroException (E1). All this is then thrown to the test function, where it is caught.

When the catch function runs, it prints the message:

E3 - Custom Exception Situation!

and then drills down through the layers of inner exceptions, printing their messages:

while (inner != null) {     Console.WriteLine("{0}",inner.Message);     inner = inner.InnerException; }

The output reflects the chain of exceptions thrown and caught:

Retrieving exception history... E2 - Func2 caught divide by zero E1 - DivideByZero Exception

As an alternative, you can call ToString( ) on the exception:

Console.Write(e.ToString());

The output reflects the entire stack of messages and the call stacks associated with them:

 RethrowingExceptions.MyCustomException: E3 - Custom Exception Situation!  ---> System.Exception: E2 - Func2 caught divide by zero --->     System.DivideByZeroException: E1 - DivideByZero Exception    at RethrowingExceptions.Test.DangerousFunc4() in c:\rethrowingexceptions\    rethrowingexceptions.cs:line 114    at RethrowingExceptions.Test.DangerousFunc3( ) in c:\rethrowingexceptions\    rethrowingexceptions.cs:line 102    at RethrowingExceptions.Test.DangerousFunc2( ) in c:\rethrowingexceptions\    rethrowingexceptions.cs:line 79    --- End of inner exception stack trace ---    at RethrowingExceptions.Test.DangerousFunc2( ) in c:\rethrowingexceptions\    rethrowingexceptions.cs:line 89    at RethrowingExceptions.Test.DangerousFunc1( ) in c:\rethrowingexceptions\    rethrowingexceptions.cs:line 61    --- End of inner exception stack trace ---    at RethrowingExceptions.Test.DangerousFunc1( ) in c:\rethrowingexceptions\    rethrowingexceptions.cs:line 71    at RethrowingExceptions.Test.TestFunc( ) in c:\rethrowingexceptions\    rethrowingexceptions.cs:line 33



Programming C#(c) Building. NET Applications with C#
Programming C#: Building .NET Applications with C#
ISBN: 0596006993
EAN: 2147483647
Year: 2003
Pages: 180
Authors: Jesse Liberty

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net