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