(Print this page)

Exceptions
Published date: Sunday, July 20, 2008
On: Moer and Éric Moreau's web site

Are all your applications handling every possible exception completely and gracefully? Probably not. And what does “completely” mean? Having a Try ... Catch into each and every method is not what I call complete coverage.

You may also want to raise your own exceptions with their own set of properties.

This is what this article is about.

Figure 1: The demo application in action

Comments on Try ... Catch ... Finally ... End Try

Whenever you want to catch exceptions, you need to use the Try ... Catch syntax. My goal here is not to show you how to use it because I really hope you already know how to use it.

I very often see developers that trap the generic exception and re-throw it without more handling. Why? If you have nothing to do, simply let the exception bubble to the caller without adding an extra handler there. Exception handling adds some overhead to the application so better use this overhead to add value to your application from it.

Every time you work with objects other than simple variables like database connection, output file, ... you should always use a Finally clause to correctly close the resources otherwise, objects may stay open if an exception occurs and your application won’t be able to reuse those resources.

The unknown When clause

A clause that is not very used but which is very useful is the When clause. This clause lets you branch the Catch expression when you need to handle the same exception but you want a more granular branching. It replaces an “If” into a catch clause. It will be clearer with this example:

Dim intCurrentStep As Integer = 0
Try
    intCurrentStep = 1
    'do something

    intCurrentStep = 2
    Me.ThrowExMethod2()

    intCurrentStep = 3

Catch ex As Exception When intCurrentStep = 0
    MessageBox.Show("An error occured while initializing (step 0)")
Catch ex As Exception When intCurrentStep = 1
    MessageBox.Show("An error occured while doing step 1")
Catch ex As Exception When intCurrentStep = 2
    MessageBox.Show("An error occured while doing step 2")
Catch ex As Exception
    MessageBox.Show("An error occured in an unknown step")
End Try

As you can see here, the same generic exception is handled 4 times but the compiler will be able to branch correctly according to the value of the variable.

A very common mistake: “Throw ex”

This is a very common error that many developers (are you one of those?) are doing without really knowing they do it. Consider this snippet of code:

Private Sub btnThrowExCommon_Click(ByVal sender As System.Object, _
                                   ByVal e As System.EventArgs) _
                                   Handles btnThrowExCommon.Click
    Try
        ThrowExMethod1CommonWay()
    Catch ex As Exception
        MessageBox.Show(MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace, _
                        "btnThrowExCommon_Click", _
                        MessageBoxButtons.OK, _
                        MessageBoxIcon.Error)
    End Try
End Sub

Private Sub ThrowExMethod1CommonWay()
    Try
        ThrowExMethod2()
    Catch ex As Exception
        Throw ex
    End Try
End Sub

Private Function ThrowExMethod2() As Decimal
    Dim x As Decimal = 1
    Dim y As Decimal = 0
    Return x / y
End Function
When you click the button, the method ThrowExMethod1CommonWay calls ThrowExMethod2 which tries to divide a number by 0 which triggers an exception. Because the ThrowExMethod2 does not handle the exception, the exception is sent to the caller procedure (which is ThrowExMethod1CommonWay) which has an exception handler that simply throws the caught exception (using Throw ex) to its own caller (the click event handler). Finally, the event handler that also has an exception handler displays the Message property of the exception along with the StackTrace property as shown in figure 2.

Figure 2: The “common-way” exception stack trace

When you read this message, you might think that the exception was raised by the ThrowExMethod1CommonWay method but you know that it isn’t the case! The help of the StackTrace property shows “The execution stack keeps track of all the methods that are in execution at a given instant. A trace of the method calls is called a stack trace. The stack trace listing provides a means to follow the call sequence to the line number in the method where the exception occurs”. This is obviously not the case here!

Now have a look at this snippet:

Private Sub btnThrowExGood_Click(ByVal sender As System.Object, _
                                 ByVal e As System.EventArgs) _
                                 Handles btnThrowExGood.Click
    Try
        ThrowExMethod1GoodWay()
    Catch ex As Exception
        MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace, _
                        "btnThrowExGood_Click", _
                        MessageBoxButtons.OK, _
                        MessageBoxIcon.Error)
    End Try
End Sub

Private Sub ThrowExMethod1GoodWay()
    Try
        ThrowExMethod2()
    Catch ex As Exception
        Throw
    End Try
End Sub
This is exactly the same pattern, a button event calling the ThrowExMethod1GoodWay that calls the very same ThrowExMethod2 method that raised the exception. The only thing that changed here (compared to the previous code snippet) is that instead of throwing the exception using “Throw ex” as we did in the previous example, we simply use “Throw”. We now have the much more complete and exact stack trace as shown in figure 3.

Figure 3: The “good-way” exception stack trace

With this stack trace, you can now really find the real method/line that caused the error.

The reason behind this is that when you write “Throw ex”, the exception is repackaged before being re-thrown and thus losing its original stack trace and restarting a new one from this point. When you simply write “Throw”, the full exception without being modified is sent to the procedure caller.

Throwing your own exception type

We sometime need to have an “intelligent” exception, an exception that can contains more than a simple message. The .Net framework lets you easily inherit the System.ApplicationException and extend it to fit your needs.

To achieve this, you first need to create a class that inherits the System.ApplicationException and which by convention should have its name ending with Exception.

This class should also have a constructor that accepts everything that will be required to initialize this exception.

Read-only public properties should also be made available to expose what is specific to this exception. You will notice in the sample below that the Message property is marked with the Overrides keyword because a property with the same name exists in the base class.

Here is an example of this class:

Public Class InvalidElementsException
    Inherits System.ApplicationException

    Private mlstElements As New List(Of String)
    Private mstrMessage As String

    Public Sub New(ByVal pMessage As String, ByVal pElements As IList)
        mstrMessage = pMessage
        For Each X As String In pElements
            mlstElements.Add(X)
        Next
    End Sub

    Public ReadOnly Property Elements() As List(Of String)
        Get
            Return mlstElements
        End Get
    End Property

    Public Overrides ReadOnly Property Message() As String
        Get
            Return mstrMessage
        End Get
    End Property

End Class
Now that you have a new exception type available, you will be able to throw it whenever needed. Here is an example of throwing an instance to this new exception class (the full code is available into the downloadable code):
Throw New InvalidElementsException( _
        "The length of those strings is invalid", _
        lstInvalidElements)
As you can see, creating new exception type is really easy, but do not abuse.

A note on the legacy “On error”

If you ever used VB6 (and earlier), you surely remember the “On Error” statement. I just want to let you know that this statement is still supported (but not really recommended) but both syntaxes to handle exception cannot appear into the same method. If you try to do so you will get a compiler error saying “Method cannot contain both a Try statement and an On Error or Resume statement”.

Conclusion

You are now ready to go back to your code and see where you can enhance your exception handling with the tricks you just learned here. And don’t forget to track down those “throw ex” lines.

I hope you appreciated the topic and see you next month.


(Print this page)