Write Robust Exception-Handling Code
Thrown exceptions break the normal flow of execution in a program to report error conditions. A few simple techniques can help you preserve execution flow and give users and administrators the information they need to understand what went wrong.
by Bill Wagner
June 13, 2007
Technology Toolbox: C#
The .NET Framework Design Guidelines has this to say about reporting errors:
"DO NOT return errors codes. … "DO report execution failures by throwing exceptions. If a member cannot successfully do what it is designed to do, … an exception should be thrown." [From Framework Design Guidelines; Additional Resources]
These few sentences have huge implications on your daily coding life. Regardless of your preferences when dealing with error reporting and error handling, your code must be robust in the face of exceptions. The .NET Framework designers have made a strong design statement: Exceptions are the correct way to report execution failures. If you call the .NET framework code (and you know you do), you need to write code that handles exceptions professionally and expertly.
In this article I'll discuss three distinct mindsets you need to keep in mind if you want to write strong error reporting and error handling code. First, I'll discuss the mindset you should have when you create libraries, and you're the one reporting errors. Second, I'll cover how to create classes that are robust when exceptions are thrown through their methods. Finally, I'll explain how to write UI logic that handles exceptions gracefully, propagating them up to the top level code.
The choice of words in the quote I've cited contains the key to understanding the proper times to throw exceptions in your code. An "execution failure" doesn't merely mean something went wrong. It's a more restrictive condition. It means the method cannot do what it was designed to do. You shouldn't be throwing exceptions every time something goes wrong. You shouldn't write brittle methods that break so easily. You can predict many things that might go wrong with a method, its inputs, its environment, or the system in which it's running. All these conditions are expected, and you should write code with these conditions in mind. Of course, you might not be able to recover from each and every one of them. Those errors that prevent your method from completing its task result in exceptions. Let's illustrate this distinction with a handful of examples.
You're probably familiar with this method:
public string GetFiles(string dir,
It's in the System.IO.Directory class, and it returns a set of files that match the search pattern in a given directory. Without looking at the docs, consider these conditions and think about what the method should do. Suppose the directory doesn't exist. What if the directory string is null? Or maybe it's an empty string. Or, the string might contain illegal path characters. Or, the string might exist, but the current account doesn't have read access. Suppose the search pattern is empty or the search pattern contains illegal characters.
Make the Right Choice
In every case, you have a choice: You can throw an exception, or you can pick some default answer. In some cases, the right approach is obvious: A directory that doesn't exist won't have any files, which makes returning an empty array a good choice. A null directory string doesn't have a good answer: Should that be interpreted as some default directory? If so, which one: the program directory, the user's home directory, C:\? There isn't an obvious answer. The method can't do what it's intended to do. And that's the point: Not every error condition is an execution error. Some conditions have obvious default answers, and you should return those default answers instead of throwing an exception. It will help your users build more robust systems.
Back to top