January 25, 2021
Hot Topics:

Don't Commit Errorcide, Part I

  • By Paul Kimmel
  • Send Email »
  • More Articles »

Errorcide occurs when an application kills itself due to the misapplication of error handling code. No error handling code and your application is doomed to the scrap heap. Too much, misapplied, or a poor application of error handling code and your error handling code causes the bugs. Perhaps the physicians' adage at least do no harm is a good way to think about error handling code.

Error handling code should make your application better but never introduce problems. In Part 1 of this two-part article, I will demonstrate the syntactical concepts of structured exception handling and how a "less is more" approach will help you make your applications more robust without introducing new errors.

Error handling is still a pretty subjective art form. It is a challenging prospect to prove that an application has too little or too much structured exception handling code. The art works well when exception handling code is applied in conjunction with problem-solving code and then is fine tuned to remediate specific problems during testing and maintenance. After reading this article, you will have a better understanding of the technical aspects of using structured exception handling, and the art will take practice. In the interim, your applications should not commit errorcide.

Basic Exception Handling Syntax

Recently, I received an e-mail from a VB6 programmer who felt betrayed by Microsoft. This reader was one of those who believed Visual Basic .NET should be called Visual Fred and assured me that he wouldn't be switching to VB.NET. I empathize. It is hard to be a master and then have to return to novitiate, especially if the calendar is long. But, Visual Basic .NET is worth switching to, and one of the reasons is structured exception handling.

VB.NET uses a class to convey information about the type of the error. Instead of a single, one-size fits all Err object, each class of error can be captured by a specific class. This permits developers to extend the error classification system to suit it for each problem domain. The number of types is unlimited. For example, if your application must know the identity of the operator or the thread the exception occurred on, the developer can implement such a class of error by sub-classing an existing exception base class. For a complete examination of the differences between VB6 structured errors and VB.NET structured exceptions, we would need another whole article. We'll leave further examination of the comparisons perhaps for another day or venue.

To begin using structured exceptions, it will be helpful to review the basic grammar of the fundamental, recommended exception handling code in VB.NET. (It is worth noting that VB6-style error handling is supported in .NET, but I suspect this will ultimately go the way of the dinosaur.) Listing 1 demonstrates the basic structure of an exception handling block.

Listing 1: A basic example of an exception handling block.

End Try

In the example, the code tries to delete a file on the B: drive. (Actually, deleting a non-existent file on a valid drive does not raise an exception. It is an acceptable operation.) The basic behavior here is that the code between Try and Catch is tried. If something goes wrong, the code between Catch and End Try runs and the error is considered handled (in the scenario shown). Think of the Try block as the man on the high-wire at the circus and the Catch block as the net below. If something goes wrong, it is nice to have that net.

Other forms of exception handlers include Try and Finally, Try, Catch, and Finally, and Try, more than one Catch, and optionally a Finally. We'll cover these variations in this article. It is important to understand at this juncture that every subroutine or function does not need, nor should have, all or even any of these elements. Some subroutines and functions will benefit from structured exception handling, and in some cases it is added noise. When to use these features is a subjective measure of good taste and is the subject of the rest of this article.

Implementing the Try Block

The Try Finally block is referred to (by some) as a resource protection block. Try Finally without a Catch block takes the following form:

  // do something that needs cleanup
  // cleanup here, always!
End Try

The Finally block is called a resource protection block because that is its purpose. Code in the Finally blocks is always run. Hence, if you need to do something such as ensure a file, socket, or database connection is closed, you want to use the Try Finally construct. The basic rhythm of the Try Finally construct is demonstrated with code and comments next:

// create a resource that you want to protect
  // try to use the protected resource
  // always clean up the protected resource
End Try

The example in Listing 2 demonstrates the rhythm of the resource protection block. In the listing, a text file is created. In the Try block, we attempt to write something to the text file. Whether the write succeeds or fails, the Finally block is executed and the file is closed.

Listing 2: A Try Finally block protecting the finite number of file handles.

Public Sub FinallyHandler()
  Dim MyTextFile As TextWriter = File.CreateText("some.txt")
    MyTextFile.WriteLine("Welcome to Valhalla Tower Material
  End Try
End Sub

The reason the resource is created outside of the Try block is two-fold. First, if we can't even create the file, an error occurs and we never enter the Try block. In the first scenario, the resource isn't created so there is no need to protect, and, trying to protect an uncreated resource can itself cause errorcide. In the example, if we declared the TextWriter outside of the Try block but created the instance inside of the Try block and the file could not be created, we get an error indicating the file couldn't be created and an error when we try to close the un-initialized TextWriter, resulting in death by error handler, or errorcide. The second reason the resource is declared outside of the try block is that the Try and Finally blocks have individuated scope. Variables declared in Try cannot be seen in Finally. Yet the subroutine in the example has an outer, containing scope and the Try and Finally blocks have inner scope. Something in the subroutine's outer scope—our resource to be protected—can be seen in the inner scope of both Try and Finally.

What happens in our example if we don't use the Finally block? The answer is that there is no guarantee that the TextWriter.Close method is called. Consequently, you may find that the text file appears to be empty. This is because closing the file flushes—programming vernacular is so colorful—execute, flush—it, ensuring that all of the text is actually written, and that the file is now available to some other process.

Page 1 of 2

This article was originally published on November 26, 2003

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date