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
Figure 2: The “common-way” exception stack trace
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
Figure 3: The “good-way” exception stack trace
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
Throw New InvalidElementsException( _ "The length of those strings is invalid", _ lstInvalidElements)
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.