If you've been writing software for a while, you're probably familiar with why you want to handle exceptions. Whenever an exception occurs in a program, if there's no exception handling, the program crashes with system-generated messages. Unfortunately, system-generated messages are often incomprehensible. Instead of system messages, you can handle exceptions and generate user-readable messages. Let's take a simple example. Listing 9-1 opens the file c:\abc.txt.
Listing 9-1: Opening a File
Imports System.IO Module Module1 Sub Main() File.Open("c:\\abc.txt", FileMode.Open) End Sub End Module
What if the file c:\abc.txt doesn't exist? Well, you get a lengthy error message, which looks like Figure 9-1.
Figure 9-1: A system-generated error
Now let's modify this program little bit to make the error message more readable. As you can see from this Listing 9-2, you handle the exception using a simple Try...Catch block.
Listing 9-2: A Simple Exception Handling Block
Imports System.IO Module Module1 Sub Main() Try File.Open("c:\\abc.txt", FileMode.Open) Catch exp As Exception Console.WriteLine("Exception Ocurred :" + exp.Message.ToString()) End Try End Sub End Module
If you run the program, the output now looks like Figure 9-2, which not only handles the exception but also reports the cause of exception.
Figure 9-2: An exception-handled error message
Using a Try...Catch statement is pretty straightforward. First, you decide what code you want the error handler to monitor. Second, you place that code inside the Try...Catch block. When an exception occurs within that block of the code, the control goes to the Catch block and handles the exception the way you've specified. Listing 9-3 shows a simple Try...Catch block.
Listing 9-3: A Simple Try...Catch Block
Try ' Place your code that may generate ' an exception in this block. Catch [optional filters] ' This code executes when the Try block fails and ' the filter on the Catch statement is true. End Try
In Listing 9-3, the optional filter describes the type of error you want to handle. For example, if you want to handle memory-related exceptions, you place a memory-related filter within the block. You'll learn about filters in the "Defining Custom Filters and Error Messages" section.
The Try...Catch...Finally construct is an extended version of the Try...Catch statement. If an error occurs during the execution of any code inside the Try section, then control moves to the Catch block when the filter condition is True. The Finally block always executes last, just before the error handling block loses scope, regardless of whether an exception occurred. The Finally block is the perfect place to clean up by closing files and disposing objects. Listing 9-4 shows a simple Try...Catch...Finally statement.
Listing 9-4: The Try...Catch...Finally Statement
Try ' Place your code that may generate ' an exception in this block. Catch [optional filters] ' This code executes when the Try block fails and ' the filter on the Catch statement is true. Finally 'Release and dispose objects here. End Try
Can you nest Try...Catch statements? Of course you can. But the only reason you may want to use nested Try...Catch blocks is when you want to catch different types of exceptions. For example, say one block is catching memory-related exceptions, one is catching Input/Output-related exceptions, and one is catching database-related errors. In that case, nesting Try...Catch statements would be beneficial.
However, you may not need to write nested statements. Actually, you can write multiple Catch statements with a single Try statement, which may solve the problem of handling multiple types of exceptions. But it doesn't mean you can't use nested statements.
The Try...Catch statement allows you to write multiple Catch statements for a Try statement. For example, you can catch memory, Input/Output (I/O), and database-related exceptions using a simple Try statement.
Listing 9-5 shows how to use nested Try...Catch blocks and multiple Catch blocks.
Listing 9-5: Using Nested Try...Catch and Multiple Catch Blocks
Imports System.IO Module Module1 Sub Main() Dim filename As String = "c:\\abc.txt" Dim Strings As New Collection() ' start first try block here Try Dim Stream As System.IO.StreamReader = _ File.OpenText(filename) ' start second Try block here Try While True Strings.Add(Stream.ReadLine()) End While Catch eosExp As System.IO.EndOfStreamException ' Write EndOfStream exception error Catch IOExp As System.IO.IOException ' IO Eception error Console.WriteLine(IOExp.Message) Strings = Nothing Finally ' Close and dispose objects here Stream.Close() End Try ' end second try bkock here ' catch of first Try block Catch exp As Exception Console.WriteLine(exp.Message) Finally ' Close and dispose objects here of first Try ' block here End Try ' end first try block here End Sub End Module
Using a Try...Catch block, you can even define your own custom filters and error messages. For example, Listing 9-6 defines a filter that fires if the counter variable is 5 or greater and then generates a custom error message.
Listing 9-6: Defining a Custom Filter and Error Message
Imports System.IO Module Module1 Sub Main() Dim filename As String = "c:\\abc.txt" Dim counter As Int16 = 10 Try Dim Stream As System.IO.StreamReader = _ File.OpenText(filename) Catch When counter > 5 Console.WriteLine("Counter Out of Range") Finally ' Close and dispose objects here of first Try ' block here End Try End Sub End Module
If you run the program, the output looks like Figure 9-3.
Figure 9-3: The custom error message