Take Control of Exceptions with a Custom Exception Object
If you really want to take full control of the exceptions you pass to your clients, you can write your own Exception class. Here's how to do that, complete with support for additional properties.
If you're going to raise an exception in your code, the decent thing to do is to pass as much information as you can to whoever (or whatever) is catching your exception. As an example, in an earlier tip, I showed how to use the InnerException property on the Exception object to pass information on the underlying exception along with your exception message.
However, in that tip, I used a particularly awful example to demonstrate using the InnerException property. Here's the example I used:
Throw New Exception("Something has gone horribly wrong", Ex)
There are at least two things wrong with my example: First (as I noted in my tip), the message gives the user no clue how to resolve the problem (a subject of another tip); second, I used the general-purpose Exception object. There's nothing wrong with the Exception object, but it conveys no additional information about the exception other than that something has gone wrong.
You should do better. If, for example, you're reporting that a bad value has been passed to a method, you should use the ArgumentException exception:
Throw New ArgumentException("Number must be positive")
But what do you do if there's no applicable Exception object to the problem your code has discovered? Surprisingly, the short answer is to create an Exception class of your own.
Creating an Exception Object
I say that creating your own Exception is the short answer because a basic Exception class has to include just three things.
First, your class must inherit from the System.Exception object.
Second, you need to provide three constructors (one that accepts a message and an exception object, one that accepts a message, and one that accepts no parameters at all). These three constructors correspond to three constructors that are part of the base Exception object so, within each constructor, you just need to use MyBase.New to pass the values accepted by your constructor to the corresponding base constructor. If you're feeling industrious, then in the default constructor (the one with no parameters) you can provide a default message by passing a message to the base constructor.
Listing 1 shows a base Exception object that, presumably, implements this pattern for exceptions related to selecting bad options. It's boilerplate code. To create a new Exception class you just need to change the name of the class and the default message.
Listing 1: A Base Exception Class
Public Class OptionsException
Public Sub New()
MyBase.New("Invalid options selected")
Public Sub New(message As String)
Public Sub New(message As String, Exception As Exception)
Extending the Exception with Custom Information
But, in the name of passing additional information, you should consider adding properties to your Exception object to hold information related to the exception. The pattern for Exception objects is that the class' properties are read-only and can only be set through the constructor.
As an example, for my OptionsException, I might want to include the option that triggered the exception. To support that I add a ReadOnly private variable to the class (ReadOnly variables can only be set from the class' constructor). Next, I wrap the variable in a ReadOnly property so that the variable's value can be read -- but not changed -- by the client application.
To support setting this variable, I add two more constructors to my class. One constructor accepts an option, a message, and an InnerException object. The other constructor accepts an option and a message. In both constructors, after calling the corresponding base constructor, I set my ReadOnly variable with the option passed to the constructor. Listing 2 shows the result:
Listing 2: An Exception Class with an Additional Property
Public Class OptionsException
Private ReadOnly _option As String
Public ReadOnly Property OptionSelected As String
Public Sub New(Message As String, BadOption As String)
_option = BadOption
Public Sub New(Message As String, BadOption As String, Exception As Exception)
_option = BadOption
What's the price of this minimal amount of work? Creating a custom Exception object, including a custom property: Roughly two dozen lines of code (and much of that code is boilerplate). Providing more information to the application and the user that catches your exception: Priceless.
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.