[ Team LiB ] |
11.4 Rethrowing ExceptionsYou 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. 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 Ukrainian dolls are contained one within the other. Example 11-8 illustrates. Example 11-8. Rethrowing inner exceptionsnamespace Programming_CSharp { using System; 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) { 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) { 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 |
[ Team LiB ] |