Exception Handling

Team-Fly    

 
Visual Basic .NET Unleashed
By Paul Kimmel
Table of Contents
Chapter 3.  Basic Programming in Visual Basic .NET

Exception Handling

Exception handling is a new feature in Visual Basic .NET. Exception handlers are intended to ultimately replace the On Error Goto construct. Exception handlers provide a more robust means of communicating and managing errors and have been available in more advanced languages like C++ and Object Pascal for several years now.

All exception classes are subclassed from the System.Exception class. The basic exception class contains information similar to that found in the VB6 Err object. The differences are that there is no global exception object always availableexception objects are created and raised when neededand in Visual Basic .NET you can subclass and define your own exception classes.

Try...Catch

The Try...Catch block is used to catch and handle errors for which you can provide a resolution. The basic Try...Catch block takes the following form:

 Try   ' some protected code Catch   ' resolution on error End Catch 

The protected code goes in the Try part of the block and the resolution part is written in the Catch block. The Try code is always executed and the Catch block is executed only in the event of an error.

VB6 simply shuts down on an unhandled error. In some circumstances it may be okay to shut down the application, but most of the time, it is another aggravation users don't need. Visual Basic .NET shuts down the application and makes an attempt to run a Just-In-Time debugger.

If an error is important enough to shut down your application, exceptions will at least provide you with an opportunity to do so in an organized manner. For example, the unavailability of a database may be a sufficient reason to shut down the application. However, when possible, if you can program a resolution to the problem and retry the code, exception handlers allow you to do that. If the problem doesn't need a resolution, you might simply want to show the error to the user and keep on trucking .

There are two kinds of exception handling. The first is referred to as the exception handling block, and the second is referred to as the resource protection block. Both are exception-handling blocks, with the difference being the keywords used to define the blocks, and their intended uses. The exception handler designed to handle errors uses the Try...Catch...End Try block and the resource protection exception handler uses the Try...Finally...End Try block. These types are demonstrated in the following sub-sections. Listing 3.8 demonstrates a generic Try...Catch block used to protect against division by zero.

Note

Recall that division-by-zero for Doubles yields a return value Infinity. Integer division-by-zero raises a DivideByZeroException.


Listing 3.8 Generic exception handler that catches all exceptions.
  1:  Public Sub TestGenericException()  2:   3:  Dim I As Integer  4:  Dim Numerator As Integer = 5  5:  Dim Denominator As Integer = 0  6:   7:  Try  8:  I = Numerator \  Denominator  9:  Debug.WriteLine(I)  10:  Catch  11:  End Try  12:  End Sub 

Lines 7 through 11 demonstrate a generic Try...Catch exception-handling block. This code can be used where you would have written an On Error Resume Next in VB6. A Catch statement with no code is referred to as a silent exception. (Basically, a silent exception works similarly to Resume Next. The reason the demonstration code contains more than I = 1/0 is because the Visual Basic .NET compiler can catch direct integer division by zero, whereas the VB6 compiler cannot.)

The Try part of the exception handler is simple enough that if we want to report the division-by-zero error, we might write a literal message that indicates what went wrong. To implement notification behavior, add something like MsgBox("Division by zero error") between the Catch and End Try lines of code.

Catching Specific Exceptions

Except when you prefer a silent exception, you will probably want to catch and respond to specific kinds of exceptions. Catching specific exceptions is supported with a slightly modified syntax.

In Listing 3.8 we know that we are anticipating a potential division-by-zero error. If we want to catch that error specifically , we need to make a minor modification to the Catch block to indicate our intentions. Listing 3.9 demonstrates catching a specific type of exception, the DivideByZeroException.

Listing 3.9 A Catch block for a specific exception.
  1:  Public Sub TestGenericException()  2:   3:  Dim I As Integer  4:  Dim Numerator As Integer = 5  5:  Dim Denominator As Integer = 0  6:   7:  Try  8:  I = Numerator \  Denominator  9:  Debug.WriteLine(I)  10:  Catch e As System.DivideByZeroException  11:  MsgBox(e.Message)  12:  End Try  13:   14:  End Sub 

The revision from Listing 3.8 to 3.9 is constrained to lines 10 and 11. The Catch statement is a declarator. Thus the revision to the Catch statement as demonstrated on line 10 declares an object of type System.DivideByZeroException and initializes it with a caught exception. The Message property of the exception object is displayed in a message dialog box.

Based on the revised definition of this Catch block, all other exceptions would be ignored. If you want to catch all exceptions, use Catch without an exception type. If you want to catch specific exceptions, list each type of exception you would like to handle with an additional Catch statement. Listing 3.10 demonstrates catching multiple exceptions.

Listing 3.10 Multiple Catch clauses.
  1:  Public Sub BackupFile(ByVal FileName As String)  2:  Dim F As System.IO.File  3:  Try  4:  F.Delete(FileName + ".bak")  5:  F.Copy(FileName, FileName + ".bak")  6:   7:  Catch e As System.UnauthorizedAccessException  8:  MsgBox(e.Message)  9:  Catch e As System.IO.FileNotFoundException  10:  MsgBox(e.Message)  11:  End Try  12:  End Sub 

Listing 3.10 demonstrates two Catch clauses. Line 7 catches the UnauthorizedAccessException that might occur when you attempt to delete or modify a read-only file. Line 9 catches the FileNotFoundException that might occur when you try to copy a file that doesn't exist.

It is impossible to include all possible ways in which the preceding code might be written. For example, you could check to see if the files existed before trying to delete and copy them. As a general rule, if an If...Then conditional check can preclude the problem, use the conditional check. However, the exception handler will catch errors you might not have thought of. In the listing you could check to see if the file exists before attempting to delete the file, but what if the file exists and is write-protected? You would still get an UnauthorizedAccessException.

Think of exception handlers as safety nets for high-wire walkers and an If...Then conditional similar to deciding whether or not you should be on the wire in the first place. If you have to be up on the wire, it's a good idea to have a net. All of the second-guessing in the world won't catch you if you fall off the wire but have decided to forego the net.

From Bjarne Stroustrup (Stroustrup 1994), the inventor of C++ and father of object-oriented languages for the PC, we know the following things about the aim of an exception handler:

  • "An exception handler isn't intended as simply an alternative return mechanism but specifically as a mechanism for supporting the construction of fault-tolerant systems."

  • "An exception handler isn't intended to turn every function into a fault-tolerant entity, but rather as a mechanism by which a subsystem can be given a large measure of fault tolerance even if its individual functions are written without regard for overall error-handling strategies."

  • And most importantly, "An exception handler isn't meant to constrain designers to a single 'correct' notion of error-handling, but to make the language more expressive."

Summarized, this means that the application of exception handling, like many things in programming, requires a subjective measure of good taste and experience.

Throughout this book I often include a statement or rationale indicating why an exception handler was used for each unique occurrence, or rationale for an exception block in code.

Raising Exceptions

Exceptions are objects. If you elect to raise an exception to indicate an error condition that a procedure is not going to handle, you create an exception object like any other object and throw it.

To create an exception instance and throw it, write code as demonstrated in the following example:

 Throw New Exception(  "message text"  ) 

Throw is a Visual Basic .NET keyword used similarly to the Raise method in VB6. New invokes the constructor for the exception class, and Exception represents any valid exception class subclassed from System.Exception.

If you want to rethrow an exception in an exception block, you can add the Throw statement all by itself in the Catch block. Throw without an explicit exception object raises the last caught exception.

Exception Filters

Catch clauses can have a filter applied that enables you to refine the Catch statement. Consider the example in Listing 3.10. Suppose we only wanted to catch the exception UnauthorizedAccessException if the file exists. We could accomplish this goal by adding a when predicate to the Catch statement:

 Catch e As System.UnauthorizedAccessException when F.Exists(FileName + ".bak") 

Due to the addition of when, this Catch clause will only catch an UnauthorizedAccessException if the backup file exists (perhaps the file exists but is read-only).

Try...Finally

Try...Finally blocks are referred to as resource protection blocks. Although Catch blocks are only invoked upon an exception of the type indicated in the Catch clausekeeping in mind that Catch without an exception predicate catches all exceptionsFinally clauses are always invoked whether there is an exception or not.

Try...Finally blocks are used to ensure that allocated resources are cleaned up. The general order of the Try...Finally statement is illustrated in this algorithmic example:

  Allocate resource (for example, open a file)   Try   Use the resource (for example, add some text to the file)   Finally   Clean up the resource   End Try  

In this example, only Try, Finally, and End Try are literals. Demonstrating the Try...Finally block using the StreamWriter class, we could create a StreamWriter, try to write some text, and close the writer in the Finally block. Listing 3.11 demonstrates both StreamWriter and the Try...Finally statement.

Listing 3.11 A resource protection block protecting a file managed by the StreamWriter object
  1:  Public Sub WriteText(ByVal FileName As String, ByVal SomeText As String)  2:   3:  Dim W As New System.IO.StreamWriter(FileName, True)  4:  Try  5:  W.WriteLine(SomeText)  6:  Finally  7:  W.Close()  8:  End Try  9:   10:  End Sub 

As described in the algorithm, the StreamWriter is allocated on line 3. Line 5 uses the protected resource to write SomeText, and the Finally block ensures that the file represented by the StreamWriter object is closed.

Try...Catch and Try...Finally blocks may be combined to protect against exceptions and resource corruption. To this end, you may nest or combine exception-handling blocks as needed for the desired effect. Consider Listing 3.11 again. What if you want to catch the UnauthorizedAccessException and ensure that opened streams were actually closed? We can combine an exception block with a resource protection block as demonstrated in Listing 3.12.

Listing 3.12 A Try...Finally block nested inside a Try...Catch block
  1:  Public Sub WriteText(ByVal FileName As String, ByVal SomeText As String)  2:   3:  Try  4:  Dim W As New System.IO.StreamWriter("sample.txt", True)  5:   6:  Try  7:  W.WriteLine("Add some text")  8:  Finally  9:  W.Close()  10:  End Try  11:   12:  Catch e As System.UnauthorizedAccessException  13:  MsgBox(e.Message, MsgBoxStyle.Critical)  14:  End Try  15:   16:  End Sub 

The outer Try...Catch block catches UnauthorizedAccessException for all of the code in between lines 3 and 12. If we get past line 4, the Try...Finally block will ensure that the open StreamWriter is closed on line 9.

You will see many examples of Try...Catch and Try...Finally blocks throughout this book. As with exception-handling blocks, I will elaborate on the rationale behind Try...Finally blocks where it is useful and not redundant to do so.


Team-Fly    
Top
 


Visual BasicR. NET Unleashed
Visual BasicR. NET Unleashed
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 222

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