Don't Commit Errorcide, Part II
Errorcide is defined as application failure caused by the absence, misapplication, or over-application of structured exception handling. In the first part of this two-part article, we talked about the fundamental mechanics of structured exception handling. In the second part—this article—we will look at more advanced mechanics and help you find a balance in the artistic dance that is useful structured exception handling.
In this article, we will look at catching specific exceptions, protecting resources, rules for exiting a Try Block, catching multiple exceptions, re-throwing and wrapping exceptions, and creating custom exception classes. Having mastered the mechanics after reading this article, you will find it much easier to decide which facet of structured exception handling to use, or whether to use any at all, in any given circumstance. It is this balance that we are ultimately working toward.
Catching a Specific Exception
An exception is a class, is a class, is a class. Instances of classes—objects—are created with the new operator and constructors, and constructors can accept a variety of parameters because overloaded constructors are supported. The notable difference is that instances of exception classes are almost universally juxtaposed with the Throw or Catch keyword. Instead of returning an error code, we throw an exception; instead of evaluating an error code prior to executing code, we catch an exception only in the presence of an error.
The Catch clause can be written without specifying a subclass of System.Exception or we can add a clause to the Catch statement and indicate a specific kind of exception to handle. The syntax of the Catch statement with a class of Exception (shown with a Try block) is:
Try ' to do something Catch(ex As Exception) ' perform some cleanup End Try
The instance is on the lefthand side of the As operator and the class of the exception is on the righthand side of the As operator. The higher up in the Exception class hierarchy the type of the class, the more kinds of exceptions a particular block will catch. The example exception handler will actually catch all exceptions, and except for the fact that we have an instance of the exception, the example Catch block works like a Catch block with no exception class indicated. If you want to eliminate some classes of exceptions, pick an exception class farther down in the hierarchy. For example, if you only want to Catch an error that occurs when a file can't be found, the type of the exception in the Catch clause would be FileNotFoundException, and all other exceptions would be ignored.
If one wanted to handle a situation where more than one kind of exception might occur, we would use multiple Catch statements. We'll talk about this more in the next section, Catching Multiple Exceptions.
Catching Multiple Exceptions
If you want to catch multiple exceptions, list multiple Catch blocks with a specific exception class in each Catch clause. The exception classes are evaluated in the order in which they appear, so if you place a more generic exception class before a more specific class, the generic class will catch the exception before the specific Catch block has an opportunity to respond. Using the FileNotFoundException in conjunction with a generic exception class the Try and Catch blocks would be written as follows:
Try ' to do something related to file io Catch(ex As FileNotFoundException) ' respond to the file not being found only Catch(ex As Exception) ' respond to a generic exception End Try
In the example, if we tried to perform an operation that threw a FileNotFoundException, our first Catch clause would respond. Any other exception in the Try block and the second Catch block would respond.
The question is not whether we can catch multiple exceptions, but should we. This is a difficult question and the answer falls into the realm of subjective opinion. That said, the answer is no almost all of the time. If you find yourself writing a lot of methods with multiple catch blocks, your methods are doing too much. I resolved upon this decision after evaluating a few considerations. The reasons include: A method should be named using a noun and verb and that name should describe a singular activity; conventional wisdom suggests that monolithic, multi-function methods are harder to build bug-free and harder to maintain; and finally, an examination of the .NET BCL (base class library) illustrates an absence of multiple catch blocks. This triad of supporting evidence is enough to help permit making a decision and moving on.
Re-Throwing an Exception
A general rule about exceptions is if you are not going to respond to the exception, don't catch it. However, respond has several connotations. One nuance of responding is to log the exception to ensure that errors are recorded in case later troubleshooting is needed.
Suppose you have a requirement to log all unhandled exceptions in the event they result in application failure. Well, if we just catch the exception and log it, the application will think it has been handled. Yet, if are only catching the exception to record it, we need to re-throw it to give some constituent further up the call stack an opportunity to handle the exception. Catching, logging, and propagating an exception could be written as demonstrated.
Try ' Simulate an error Throw New Exception("something is very very wrong here") Catch ex As Exception EventLog.WriteEntry("Application", ex.Message) Throw End Try
In the Try block, we intentionally throw an exception to simulate an error. In the Catch block, we write the exception to the event log using the shared method WriteEntry of the EventLog class and then use the Throw statement. Throw without a specific exception object re-throws an exception in the current context. In the example, this is the exception in the current catch block. Catching an exception to record it is a legitimate partial response, but if we don't re-throw the exception, it is considered handled, which is a good precursor to errorcide.