Home > Articles > Microsoft > Other Microsoft

  • Print
  • + Share This
This chapter is from the book

Handling Exceptions

One of the most fundamental and often most complex tasks of a developer is to design exception-handling procedures in order to allow an application to recover gracefully from an unexpected or disallowed condition.

The .NET Framework allows exception handling to be performed between languages, across multiple computers, and even through integration with COM and other legacy solutions. Within Visual Basic .NET, the most common methods of exception handling involve the Try, Catch, and Finally blocks in addition to the Throw statement.

TIP

The Try, Catch, and Finally blocks must be consecutive within code. You may not have intervening code separating these blocks when configuring event-handling solutions within your code. Everything between the Try and End Try keywords is part of a single error-handling unit.

The Try Block

When code is processed that may generate an exception, the code may be placed within a Try block, which must in turn be followed by one or more Catch blocks or a Finally block. Exceptions raised cause the CLR to search for the nearest Try block (which may be nested within one another) and then pass control to the following Catch block or on to the Finally block if no Catch block is present in the code.

A Try block in its most basic form looks like this:

Try
  ' Code that may cause an exception
Catch
  ' Code that handles any exception
End Try

Any normal code may be placed within the Try block, including another Try block or calls to methods including Try blocks of their own. When an exception occurs, the CLR will find the nearest Try block and pass control to the following associated exception-handling blocks.

CAUTION

A Try block must be followed by one or more Catch blocks or by a Finally block.

The Catch Block

After an exception is encountered within a Try block, control is passed to the following Catch blocks or to the Finally block. Catch blocks are evaluated in order until a match to the exception type is made. A particular exception might be matched by many different Catch blocks of varying levels of generality.

For example, a DivideByZeroException might match a Catch block for DivideByZeroException, ArithmeticException, SystemException, or Exception. These are all increasingly general exception classes that contain the specific DivideByZeroException. If no matching Catch block can be found, the exception is passed back to the code that raised the exception and is considered an unhandled exception, which is discussed in greater detail later in this chapter.

NOTE

The default behavior for an unhandled exception is to cause the program to terminate with an error message.

TIP

Because Catch blocks are evaluated in the order that they occur in your code, you must always place the most specific exceptions first if you have multiple Catch blocks. Otherwise, the code will not compile.

A Try block followed by some Catch exception blocks might look like this:

Private Sub btnCalculate_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles btnCalculate.Click
  Try
    Dim decMiles As Decimal = Convert.ToDecimal(txtMiles.Text)
    Dim decGallons As Decimal = Convert.ToDecimal(txtGallons.Text)
    Dim decEfficiency As Decimal = decMiles / decGallons
    txtEfficiency.Text = String.Format("{0:n}", decEfficiency)
    ' each try block should at least have one catch or finally block
    ' catch blocks should be in order of specific to the generalized
    ' exceptions. Otherwise compilation generates an error
  Catch fe As FormatException
    Dim msg As String = String.Format("Message: {0}" & _
     vbcrlf & "Stack Trace: " & vbcrlf & " {1}", _
     fe.Message, fe.StackTrace)
    MessageBox.Show(msg, fe.GetType().ToString())
  Catch dbze As DivideByZeroException
    Dim msg As String = String.Format("Message: {0}" & _
     vbcrlf & " Stack Trace: " & vbcrlf & " {1}", _
     dbze.Message, dbze.StackTrace)
    MessageBox.Show(msg, dbze.GetType().ToString())
    ' catches all CLS-compliant exceptions
  Catch ex As Exception
    Dim msg As String = String.Format("Message: {0}" & _
     vbcrlf & " Stack Trace: " & vbcrlf & " {1}", _
     ex.Message, ex.StackTrace)
    MessageBox.Show(msg, ex.GetType().ToString())
    ' catches all other exceptions including
    ' non-CLS-compliant exceptions
  Catch
    ' just rethrow the exception to the caller
    Throw
  End Try
End Sub

Here, an exception raised within the code is thrown to the Catch blocks in order to see if a match is made. The inclusion of the Catch block that uses the general Exception class will prevent an unhandled exception. When this code is compiled and run, an input zero value (DivideByZeroException), a nonnumeric value (FormatException), or a very large numeric value (OverflowException, caught by the Catch block as an exception) will not cause the program to terminate. A message will be returned to the user and the application will continue to function.

All languages that follow the Common Language Specification (CLS) will generate an exception that derives from System.Exception. The final Catch block in the code example (which does not specify an exception type at all) is included to handle exceptions generated by possible non–CLS-compliant languages. An unspecified Catch block is the most general form of Catch possible and should always be the last Catch block if multiple Catch blocks are present, because the first matching Catch block will be the one performed.

The Finally Block

The Finally block includes code that will run regardless of whether an exception is raised. This is a good location for cleanup code that will close open files or disconnect database connections to release held resources.

The following is an example of code that includes a Finally block:

Private Sub btnSave_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles btnSave.Click
 ' A StreamWriter writes characters to a stream
 Dim sw As StreamWriter
 Try
   sw = New StreamWriter(txtFilename.Text)
   ' Attempt to write the textbox contents in a file
   Dim strLine As String
   For Each strLine In txtText.Lines
     sw.WriteLine(strLine)
   Next
   ' This line only executes if there were no exceptions so far
   MessageBox.Show("Contents written, without any exceptions")
   ' Catches all CLS-compliant exceptions
 Catch ex As Exception
   Dim msg As String = String.Format( _
   "Message: {0}" & vbcrlf & " Stack Trace: " & vbcrlf & _
   " {1}", ex.Message, ex.StackTrace)
   MessageBox.Show(msg, ex.GetType().ToString())
   GoTo endit
   ' The finally block is always executed to make sure that the
   ' resources get closed whether or not an exception occurs.
   ' Even if there is a goto statement in a catch or try block the
   ' finally block is first executed before the control goes to the label
 Finally
   If Not sw Is Nothing Then
     sw.Close()
   End If
   MessageBox.Show( _
   "Finally block always executes whether or not exception occurs")
 End Try
EndIt:
  MessageBox.Show("Control is at label: end")
End Sub

The Finally block may also be used in conjunction with a Try block, without any Catch blocks. Exceptions raised would be returned as unhandled exceptions in this case. An example of the simplest form of this follows:

Try
  ' Write code to allocate some resources
Finally
  ' Write code to Dispose all allocated resources
End Try

The Throw Statement

The Throw statement can be used to explicitly throw an exception within your code. It can be used in order to re-throw a caught exception to perform a task such as generating an Event Log entry or sending an email notification of the exception, or it may be used in order to raise a custom exception explicitly defined within your code.

NOTE

Exceptions should only be handled or explicitly thrown when doing so improves application usability and robustness. Evaluation of exception-handling code adds to the resource requirements needed to run your application and can rapidly become "too much of a good thing."

Here's an example of a Throw statement in its simplest form being used to re-throw a caught exception:

Catch ex As Exception
  ' TODO: Add code to write to the Event Log
  Throw

To use the Throw statement with a custom error message, you may create something like this:

Dim strMessage As String = "EndDate should be greater than the StartDate"
Dim exNew As ArgumentOutOfRangeException = _
 New ArgumentOutOfRangeException(strMessage)
Throw exNew

NOTE

The .NET Framework CLR includes many standard exception types, but on occasion it is necessary or desirable to create a custom exception to address a specific need within your code. This should be done only if there is not already an existing exception class that satisfies your requirements. Any new custom exception should use the System.ApplicationException class as its base class.

  • + Share This
  • 🔖 Save To Your Account