Documenting Exceptional Developers
By Peter A. Bromberg, Ph.D.
Printer - Friendly Version
Peter Bromberg

Two things developers never seem to have time for are documentation and error handling. Documentation -- well, C#, along with the HTML Help Workshop help compiler and free tools like NDoc make creating MSDN-quality full text searchable CHM help files easy and fun. Error handling -- that's another story. The biggest offender is the empty catch block:

try
{
// Your crappy buggy code here
}
catch (Exception)
{ // Duh, nada! }

// yippee! jes keep right on truckin!


This is a poor excuse for error handling, its plain BAD programming practice, and it can also cause PERFORMANCE PROBLEMS. I spent a whole bunch of time up at the MS Testing lab in Charlotte, and all the gurus there told me the same thing: Avoid having exceptions in the normal operation of your .NET programs. And, they showed me AD Plus and Mutex Black Box traces of why it was bad, too. Those reports were UGLY - especially when we saw how many CPU cycles our crappy, exception-generating code was burning up!

Use Exceptions Wisely

You should only throw exceptions when a condition outside of your code's assumptions occurs. In other words, don't use exceptions as a way to provide your intended functionality even if something bad happens.

For example, a user might enter an invalid user name or password while logging onto an application. While this is not a successful logon, it should be an expected result, and therefore it should not throw an exception. Same with loading an XmlDocument, or reading from a file. In most (not all) cases, it is possible to construct one's code so that it is never necessary to have the ugly code shown above. Many failure conditions from .NET methods return null. You don't need to have an empty catch block to test for that. There are other methods such as double.TryParse() which return true or false (as opposed to double.parse(), which throws an exception) and with which it is possible to construct much more elegant and functional code.

However, an exception should be generated if a real, legitimate unexpected condition occurs, such as an unavailable user database. Throwing or allowing exceptions to be thrown is more expensive than simply returning a result to a caller or having code to handle an expected condition. Therefore exceptions should not be used to control the normal flow of execution through your code.

Excessive use of exceptions can create unreadable and unmanageable code, and cause a nasty condition called stress headache to other developers who must read through one's code.

Design your classes so that an exception is not thrown in normal use. For example, a FileStream class exposes
a way of determining whether the end of the file has been reached. This avoids the exception that is thrown if you read past the end of the file. The following example shows how to read to the end of the file.

class FileRead {
void Open() {
FileStream stream = File.Open("myfile.txt", FileMode.Open);
byte b;

// ReadByte returns -1 at EOF.
while ((b == stream.ReadByte()) != true) {
// Do something. No Exceptions!
}
}
}


How the Runtime Manages Exceptions

An exception is any error condition or unexpected behavior encountered by an executing program. Exceptions can be raised because of a fault in your code or in code you call (such as a shared library), operating system resources not being available, unexpected conditions the common language runtime encounters (such as code that cannot be verified), and so on. Your application can recover from some of these conditions, but not others. While you can recover from most application exceptions, you cannot recover from most runtime exceptions.

In the .NET Framework, an exception is an object that inherits from the Exception Class class. An exception is thrown from an area of code where a problem has occurred. The exception is passed up the stack until the application handles it or the program terminates. For more information on handling exceptions using the .NET Framework, see The Exception Class in the .NET Documentation.

The runtime uses an exception handling model based on exception objects and protected blocks of code. An Exception object is created to represent an exception when it occurs.

The runtime creates an exception information table for each executable. Each method of the executable has an associated array of exception handling information (which can be empty) in the exception information table. Each entry in the array describes a protected block of code, any exception filters associated with that code, and any exception handlers (catch statements). This exception table is extremely efficient and there is no performance penalty in processor time or in memory use when an exception does not occur. You use resources only when an exception occurs.

The exception information table represents four types of exception handlers for protected blocks:

  • A finally handler that executes whenever the block exits, whether that occurs by normal control flow or by an unhandled exception.
  • A fault handler that must execute if an exception occurs, but does not execute on completion of normal control flow.
  • A type-filtered handler that handles any exception of a specified class or any of its derived classes.
    A user-filtered handler that runs user-specified code to determine whether the exception should be handled by the associated handler or should be passed to the next protected block.
  • Each language implements these exception handlers according to its specifications. For example, Visual Basic .NET provides access to the user-filtered handler through a variable comparison (using the When keyword) in the catch statement; C# does not implement the user-filtered handler.

When an exception occurs, the runtime begins a two-step process:

The runtime searches the array for the first protected block that:
Protects a region that includes the currently executing instruction, and
Contains an exception handler or contains a filter that handles the exception.

If a match occurs, the runtime creates an Exception object that describes the exception. The runtime then executes all finally or fault statements between the statement where the exception occurred and the statement handling the exception. Note that the order of exception handlers is important: the innermost exception handler is evaluated first. Also note that exception handlers can access the local variables and local memory of the routine that catches the exception, but any intermediate values at the time the exception is thrown are lost.

If no match occurs in the current method, the runtime searches each caller of the current method, and it continues this path all the way up the stack. If no caller has a match, the runtime allows the debugger to access the exception. If the debugger does not attach to the exception, the runtime raises the UnhandledException event. If there are no listeners for the UnhandledException event, the runtime dumps a stack trace and ends the program.

In most of the .Net projects I've worked on, we've included special logging and exception-handling classes that make the work of wiring up, logging, and tracing errors and exceptions more object-oriented. One well-known code example is the Microsoft Application Blocks Exception Handling block, which should serve as an excellent starting point for beginning to intermediate-level developers. Another excellent open-source offering is Log4Net, an extensive and easy-to- use set of logging classes that's been ported from the JAVA space.

Document your classes, and take the time to be exceptional coder!


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.