Try, Catch, and Finally

Try, Catch, and Finally

When debugging your code and planning where and how to implement error handling, you start by examining your program code and determining which sections of code might throw exceptions. These sections are placed in a Try block. The code designed to handle exceptions generated in the Try block are placed in a Catch block. The Catch block is a series of statements beginning with the keyword Catch, followed by an exception type and an action to be taken. The Finally construct will always execute and can be used for clean-up operations such as closing files or connections.

This manner of handling errors is known as structured exception handling. To use this powerful .NET feature, we simply place any code that might fail inside the Try…Catch block. With this model, we get quite a bit of functionality, as shown in Figure 7-3.

Figure 7-3

Structured exception handling in the .NET Framework.

Structured exception handling is a service that's built into the core of .NET, so it's available to all languages that target the .NET platform. As you might guess, structured error handling is located in the common language runtime. Very little code is required to fully utilize the features of structured error handling.

When used correctly, structured exception handling in Visual Basic .NET allows your program to detect and usually recover from errors during execution. Visual Basic .NET uses an enhanced version of the Try…Catch…Finally syntax already supported by other languages. This form of structured exception handling combines a modern control structure (similar to Select Case or While) with exceptions, protected blocks of code, and filters.

Structured exception handling is the method recommended for .NET applications and makes creating and maintaining programs with robust, comprehensive error handlers easy. Exceptions that are not caught by a Catch block are handled by the common language runtime, but having the runtime handle exceptions is a bad idea. We want to keep it from happening at all costs. Depending on how the common language runtime is configured, having it handle an exception could result in a debug dialog box appearing or in a dialog box appearing with the exception information and the program abruptly stopping execution. These events are not considered good programming form.

As I mentioned earlier, programs need to uniformly handle errors and exceptions that occur during execution. The design of error-tolerant software is greatly assisted by the CLR. The CLR provides a platform for notifying programs of errors in a consistent way, even if the error occurs in a module written in a language different from the language used to write the module catching the error. All .NET Framework methods indicate failure by throwing exceptions. You can now easily catch them when they are thrown and handle them gracefully. Let's see how to add structured error handling to our code.

Adding Structured Error Handling

Open the Throwback Calculator program created in Chapter 6 and add the following lines to it. Remember what I said about devoting more than 60 percent of your code to error handling? This example illustrates the point nicely.

Try    iInput = int32.Parse(txtInput().Text)     For iCounter = Arabics.GetUpperBound(0) To _         Arabics.GetLowerBound(0) Step –1         While iInput >= Arabics(iCounter)             iInput -= Arabics(iCounter)             sOutPut += Romans(iCounter)         End While     Next     lblResult().Text = sInput & " = " & sOutPut     Catch ex As System.Exception When Not _         IsNumeric(txtInput().Text)         MessageBox.Show(sInput & " is not a valid number. " & _             "Please reenter.", sProgName, MessageBoxButtons.OK, _             MessageBoxIcon.Hand)         lblResult().Text = ""     Catch ex As System.FormatException         MessageBox.Show(sInput & " is not an integer. " & _             "Please reenter.", sProgName, MessageBoxButtons.OK, _             MessageBoxIcon.Hand)         lblResult().Text = ""     Catch ex As System.OverflowException         MessageBox.Show(sInput & " is too large or small. " & _             "Please enter an integer from 1 to 9999.", _             sProgName, MessageBoxButtons.OK, _             MessageBoxIcon.Hand)         lblResult().Text = ""     Catch ex As System.Exception         MessageBox.Show(ex.GetType.ToString, sProgName, _                MessageBoxButtons.OK, MessageBoxIcon.Hand)         lblResult().Text = ""     Finally         txtInput().Clear()         txtInput().Focus() End Try

The Try…Catch Block

The common language runtime supports an exception-handling model that's based on exception objects and protected blocks of code. When an exception occurs, an object is created to represent the exception. Remember that run-time errors do not cause your program to crash, but unhandled run-time errors do. The .NET CLR indicates that an error condition has occurred by executing a Throw statement. The statement throws an object of a type derived from System.Exception. This object provides information about the specific error that has occurred.

If an expression does not evaluate to an instance of a type derived from System.Exception, an error occurs at compile time. If at run time an expression evaluates to a null reference, an instance of a System.NullReferenceException object is thrown instead.

As I mentioned, the sections of code that might throw exceptions (errors) are placed in a Try block. The code designed to handle exceptions generated in the Try block are placed in a Catch block. The Catch block is a series of statements beginning with the keyword Catch, followed by an exception type and an action to be taken.

If an exception is thrown within the Try block, we want to catch it and handle it. In our example, the first Catch clause will fire if a System.Exception is thrown when the value entered by the user is not numeric. This Catch clause uses a filter to trap when an exception results from a non-numeric value. If we didn't add a filter to specify a particular kind of error, the Catch statement would handle each and every instance of System.Exception, including all three types we're anticipating.

Of course, we could use a single Catch statement to handle all System.Exception objects. However, an unfiltered Catch statement would not provide the user with helpful information about what happened. Because one of the goals in professional software development is to provide a user with useful and nonthreatening information about the type of error and how to correct it, we've added some detail by using several Catch clauses to handle specific errors. In addition, rather than displaying a cryptic .NET runtime message, we want to provide users with an informative message explicitly telling them what went wrong. If a user types a value such as 123Q, our code will handle that particular exception.

Our first Catch block will handle only exceptions thrown when the contents of the input text box are not numeric.

Catch ex As System.Exception When Not _     IsNumeric(txtInput().Text)     MessageBox.Show(sInput & " is not a valid number. " & _         "Please reenter.", sProgName, MessageBoxButtons.OK, _         MessageBoxIcon.Hand)      lblResult().Text = ""

Now we can show the user what they entered, tell them it's not a valid number, and ask them politely to try once more, as you can see in Figure 7-4.

Figure 7-4

The first Catch block gracefully handles nonnumeric input.

If some other exception is thrown, the program will bypass the first Catch block and go to the next one to see whether it can handle the exception, and so on. We might also want to provide an informative message if a user accidentally types a noninteger, such as 123.45. Here's the Catch statement that handles this exception. The error message is shown in Figure 7-5.

Catch ex As System.FormatException     MessageBox.Show(sInput & " is not an integer. " & _         "Please reenter.", sProgName, MessageBoxButtons.OK, _         MessageBoxIcon.Hand)     lblResult().Text = ""

Figure 7-5

The second Catch block gracefully handles non-integer values.

Another problem could be that the user enters a very large or small number. Again, we can indicate what's wrong about the data the user entered and provide some guidance, as you can see in Figure 7-6.

Catch ex As System.OverflowException     MessageBox.Show(sInput & " is too large or small. " & _         "Please enter an integer from 1 to 9999.", _         sProgName, MessageBoxButtons.OK, _         MessageBoxIcon.Hand)      lblResult().Text = ""

Figure 7-6

The third Catch block handles very large and very small values.

When writing your Catch clauses, always write the most specific one first and work your way down to the most general. Following this order allows you to handle a specific exception before it's passed to a more general Catch block.

Making Our Simple Program Even More Bullet Proof

To put another level of protection in our program, we can easily limit the number of characters a user can enter in a text box. Bring up the properties box for the txtInput textbox and set the MaxLength property to 4, as shown in Figure 7-7. This setting ensures that the largest number a user can enter is 9999. With this protection, the third Catch statement we included is never executed.

We all know that exceptions that we can't predict can be thrown; for example, errors caused by bad hardware, having no memory left, and the like. To handle an error that has not been anticipated, you can use a default Catch statement to handle any exceptions not caught by other, more specific statements. In the Throwback program, our default Catch statement simply prints out the exception and our program continues unfazed. By using a Catch statement, we ensure that the program won't crash and burn.

Figure 7-7

Visual Basic .NET lets you limit user input.

Catch ex As System.Exception     MessageBox.Show(ex.GetType.ToString, sProgName, _            MessageBoxButtons.OK, MessageBoxIcon.Hand)     lblResult().Text = ""

You can derive classes from System.Exception and even add additional properties and methods to better explain the error to the code that catches the exception. Two classes that inherit from the System.Exception class are System.Application (for implementing errors thrown by the application and not the CLR) and System.SystemException, which is the base class for all predefined exceptions in the System namespace.

The most severe exceptions thrown by the common language runtime are usually nonrecoverable, including ExecutionEngineException and StackOverflowException. It's not recommended that you throw or catch these two severe errors.

tip

In your programs, you can actually throw your own exceptions. You can programmatically throw as an exception any object that derives from the Object class. Developers can create their own exception classes by subclassing the appropriate base exception. You might wonder why in the world you would ever want to throw your own exception. The reason is simple. You could create a class that throws many types of exceptions to see how your program reacts. You could add this class to your programs during your testing. In the class, a timer would throw all sorts of exceptions and you could monitor how your program handles them. Following this approach, you could be pretty sure the final release version was rock solid. Just before release, you would remove the class from the production code.

Another use of a class such as this is to throw an error if a condition is met that will be handled by a Try…Catch block located elsewhere in the program. In this way, you can maintain centralized error control.

The Finally Block

The Finally block is always run when program execution leaves any part of the Try statement. No explicit action is required on your part to execute the Finally block—when execution leaves the Try statement, the system will automatically execute the Finally block.

Execution of the Finally block occurs regardless of the method in which a program leaves the Try statement. The Finally block can be reached successfully from the end of the Try block, by reaching the end of a Catch block, or by executing an Exit Try statement. The Finally block is usually used for clean-up operations. It is invalid to explicitly transfer execution to a Finally block.

We saw in Figure 7-3 that the result of throwing an exception is that the call stack is unwound. If we have a database connection or are using another expensive resource, it will not be finalized and disposed of when the stack is unwound. This problem can be solved by a Finally block. Code in the Finally block always executes just before the stack is unwound, so this block is a good place to add any resource finalization code you might have.

In our program, we execute the Clear method of the text box in the Finally block to empty its contents for the next round of user input. We also force focus to the text box so that if the user enters another number, it will be added to the text box automatically—the user will not have to first click the box to give it focus. This detail is a particularly nice touch in professional code.

Finally     txtInput().Clear()     txtInput().Focus() End Try

A Structured Exception Gotcha

Remember that the Finally block always executes no matter what. Many novices find the Finally block a bit confusing. Be careful to structure a Try block to handle the code you want to execute and use the Finally block to perform only operations that will always occur, such as cleanup. In the following code, no matter what number you pass as a parameter, and whether an error occurs or not, the function will always return 0.

Private Function squareNumber(ByVal intToSquare As Integer) _     As Long     Dim result As Long     Try         result = intToSquare * intToSquare     Catch ex As System.Exception         MessageBox.Show("There was a slight problem.")     Finally         result = 0     End Try     Return result End Function

You could easily test this code by calling the function and displaying the result in a message box. Of course, in our squareNumber function, we defined the data type of the number to accept as an integer. If you attempted to pass a number such as 5.5, the compiler would bark at you and report "Option Explicit disallows implicit conversions from Double to Integer." So strong typing helps you pass the correct data type. But if the logic of the function is malformed, you'll still receive the wrong answer, as shown here.

MessageBox.Show(squareNumber(5).ToString)  'prints 0

To make this function work correctly, you should place the result = 0 line in the Catch clause. Making this change would return 25, which is what we expected.



Coding Techniques for Microsoft Visual Basic. NET
Coding Techniques for Microsoft Visual Basic .NET
ISBN: 0735612544
EAN: 2147483647
Year: 2002
Pages: 123
Authors: John Connell

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net