Previous section   Next section

11.2 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.

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
Option Strict On
Imports System

Namespace Programming_VBNET
    Public Class MyCustomException
        Inherits System.ApplicationException

        Public Sub New(ByVal message As String, ByVal inner As Exception)
            MyBase.New(message, inner)
        End Sub 'New
    End Class 'MyCustomException

    Public Class Test

        Public Shared Sub Main( )
            Dim t As New Test( )
            t.TestFunc( )
        End Sub 'Main

        Public Sub TestFunc( )
            Try
                DangerousFunc1( )

                ' if you catch a custom exception
                ' print the exception history
            Catch e As MyCustomException
                Console.WriteLine(ControlChars.Lf + "{0}", e.Message)
                Console.WriteLine("Retrieving exception history...")
                Dim inner As Exception = e.InnerException
                While Not (inner Is Nothing)
                    Console.WriteLine("{0}", inner.Message)
                    inner = inner.InnerException
                End While
            End Try
        End Sub 'TestFunc

        Public Sub DangerousFunc1( )
            Try
                DangerousFunc2( )

                ' if you catch any exception here
                ' throw a custom exception
            Catch e As System.Exception
                Dim ex As New MyCustomException( _
                 "E3 - Custom Exception Situation!", e)
                Throw ex
            End Try
        End Sub 'DangerousFunc1

        Public Sub DangerousFunc2( )
            Try
                DangerousFunc3( )

                ' if you catch a DivideByZeroException take some 
                ' corrective action and then throw a general exception
            Catch e As System.DivideByZeroException
                Dim ex As New Exception( _
                    "E2 - Func2 caught divide by zero", e)
                Throw ex
            End Try
        End Sub 'DangerousFunc2

        Public Sub DangerousFunc3( )
            Try
                DangerousFunc4( )
            Catch e As System.ArithmeticException
                Throw e

            Catch e As System.Exception
                Console.WriteLine("Exception handled here!")

            End Try
        End Sub 'DangerousFunc3

        Public Sub DangerousFunc4( )
            Throw New DivideByZeroException("E1 - DivideByZero Exception")
        End Sub 'DangerousFunc4
    End Class 'Test
End Namespace 'Programming_VBNET
Output:
E3 - Custom Exception Situation!
Retrieving exception history...
E2 - Func2 caught divide by zero
E1 - DivideByZeroException

Because the code in Example 11-8 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:

Public Sub TestFunc( )
    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, you pass in the custom message E1 - DivideByZeroException.

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:

Public Sub DangerousFunc3( )
    Try
        DangerousFunc4( )
    Catch e As System.ArithmeticException
        Throw e

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:

Catch e As MyCustomException
    Console.WriteLine(ControlChars.Lf + "{0}", e.Message)
    Console.WriteLine("Retrieving exception history...")
    Dim inner As Exception = e.InnerException
    While Not (inner Is Nothing)
        Console.WriteLine("{0}", inner.Message)
        inner = inner.InnerException
    End While
End Try

The output reflects the chain of exceptions thrown and caught:

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

  Previous section   Next section
Top