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.
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 and inner exceptions
namespace Programming_CSharp
{
using System;
public class MyCustomException : System.Exception
{
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("
{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.
You 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
, passing to the constructor a
new string (E3 - Custom Exception Situation!
) and
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
18.117.99.152