Perhaps to maintain a respectable distance from unstructured error handling, Visual Basic .NET prefers to refer to structured error processing as structured exception handling. Structured exception handling is designed to enable the programmer to isolate the code that processes errors. By contrast, it's not uncommon to see code like this:
If N <> 0 Then Y = X / N Else MessageBox.Show("Divide by 0 error") End if
In this code fragment, the programmer is trying to guard against a divide by zero error. Although this is okay, it tends to mix the error processing code with the non-error code, making the program more difficult to read. Also, if there is a divide by zero error, the preceding code fragment doesn't do anything after the error message is displayed. (You'll often see an Exit Sub as the statement line after the message box is displayed.)
Structured exception handling tries to isolate the code that handles the errors from the main program flow. Further, structured exception handling attempts to do this in an organized fashion, using the following syntax structure:
Try TryStatementBlock Catch [ parameter ] CatchStatementBlock [Finally] FinallyStatementBlock End Try
I'll discuss each of these keywords in the following sections. After that discussion, I'll present a code example that will help you understand how each statement block may be used.
The Try Statement Block
The Try statement block contains the code that you think could cause an exception to be generated. The statements that appear within the block look like any other Visual Basic .NET program statements. The only difference is that Visual Basic .NET tries to execute those statements within the protective shell created by the Try - Try End keywords.
Deciding where you should place the Try keyword depends on what you're trying to do. If you're writing code that you know might cause a problem, encapsulate those statements within a Try statement block.
The Catch Statement Block
There are two flavors of the Catch statement. The first flavor is a Catch that does not use the optional parameter list. This is often referred to as a generic Catch . The second flavor does have a parameter list and is referred to as a parameterized Catch statement. Let's examine each of these in a little more detail.
Generic Catch Statement
If you use Catch by itself, you're saying that the Catch statement block should handle all exceptions that might be generated by the Try code. A simple code fragment using this flavor might be
Try ' Some statements that might generate an error Catch MessageBox.Show("Something bad happened") End Try
As you can see from the code fragment, the Catch statement block is a generic exception handler that catches all errors that might occur in the Try block.
This code suffers from the dreaded Jack-of-All-Trades syndrome. That is, the exception handler does work and traps all exceptions, but it doesn't do its job very well. Users are not going to find the message Something bad happened terribly useful. But that's the nature of the beast . If you catch everything, the error message (or corrective action) by definition must be fairly generic.
Parameterized Catch Statement
The second flavor offers more hope. In this case, we add an optional parameter to the Catch statement. For example, we might try something like this:
Try Dim i as Integer i = i / 0 ' Cause a divide by zero error ' More statements in the Try statement block Catch e as DivideByZeroException MessageBox.Show("Data produced a divide by zero error") End Try
In this case, the Catch statement has an exception parameter that specifically tests for the exception named DivideByZeroException . The variable e is called the exception parameter and holds the value of the exception as determined by Visual Basic .NET.
So, where did the DivideByZeroException parameter come from? Visual Basic .NET provides a detailed list of the exceptions that it can detect. To see the broad classifications of exceptions, select the Debug-Exceptions menu sequence or Ctrl+Alt+E. The list is shown in Figure 19.13.
Figure 19.13. The Visual Basic .NET exceptions list window.
Although you can look at each of these by clicking on its plus box, we're primarily interested in the exceptions found in the common language runtime. If you click on the plus box for the common language runtime, you'll see a list of all the exceptions Visual Basic .NET can recognize in the common language runtime. At the present time, we are interested in the exceptions for the System object, so click on its plus sign. You should see a list similar to that shown in Figure 19.14.
Figure 19.14. A list of the System object's exceptions.
If you look closely, you can see the DivideByZeroException defined in the list. You can use any of these exception names in a Catch statement to handle that specific exception.
Clearly, a parameterized Catch can probably do something more useful from the user 's point of view than can a generic Catch . The error message can be more specific or perhaps some form of corrective action can be initiated. However, as we'll see in a moment, a generic Catch is better than no Catch at all.
The Finally Statement Block
The Finally statement block isn't required in structured exception handling. The Finally statement block is used to hold code that is always executed, even if no exception is thrown by the Try block. However, the Finally code is also executed after any Catch statement block should an exception occur. Exception or no exception, the code in the Finally block is executed.
The Finally block enables you to do any cleanup code that must take place before leaving the procedure. This ability obviates the need for a Resume -type branch after an exception is encountered .
The End Try Statement
The End Try statement simply marks the end of an exception-handling statement block. When the End Try statement is reached, any exception handling associated with the Try statement block is no longer in effect. You may have more than one Try - End Try statement block in a procedure and you can nest exception-handling blocks within a procedure.
One last point to notice about structured exception handling: It promotes a top-down program flow. Unstructured error handling often used a Resume statement to jump back somewhere in the code, making it more difficult to follow the program flow. Structured exception handling has a smoother flow to it, from top to bottom, and has no need to jump back to some previous section of code. Smooth is better.
Now that we know the basics of structured exception handling, let's try implementing what we've learned.