«  Friday Links #6 | FreeSnap 1.3 and Desk Dri... »

To Throw or not to Throw, that is the question

I find it frustrating that there are programmers I work with who still prefer to use error return codes or even worse, asserts, to report errors. We work almost exclusively in .Net here so from my perspective, the decision is clear -- use exceptions. The framework only reports errors using exceptions. That alone should end the argument. Why introduce a second mechanism?

The two most frequent arguments I get for not using exceptions are:

  • Exceptions will cause the program to crash if they're not handled
  • Exceptions make my code ugly

The question to ask yourself is, "If the method can not honor its contract, should it report the error?" I think most people would agree the method should report any errors that prevent it from completing its mission. If you have a method that says it's going to open a file for reading and it can't, by golly it better tell the caller. If you don't agree, you can stop reading now.

Seriously, if your method can't complete it's mission after exhausting all means, then it has to report the problem. Ok, maybe we're in agreement so far. Where the friction begins is how to report the error. Some say to log it and move on. Others say use an assert and test your code. Some like error return codes.

Logging is nice from the perspective that it allows you to see what happened after the fact. But it does not allow your program to respond to the error since it doesn't know what happened.

Asserts seem like a nice option. The theory is that the programmer will see the issue during testing and fix it on the spot. From my own experience, it simply doesn't happen and you end-up hitting OK twenty times just to get your program started. Never mind they do nothing in release mode and of course the caller doesn't get told about the error.

Return codes at least allow the caller to check for errors. Of course if the caller forgets to check, the error goes undiscovered. And then there's the matter of the codes themselves. They have to be documented to know what they mean. Anyone remember programming using native Win32 API's? The same error code meant something different depending on what component was called. No thank you.

Furthermore, other than logging, none of these mechanism's give you rich error reporting. A stack trace and additional information can be contained an Exception (a .Net exception at least) which is invaluable when trying to diagnose a problem.

Exceptions (again I'm talking .Net exceptions) allow (actually force) the caller to take action. And in addition, specifics of the problem can be captured in the exception itself.

Still there's that issue of unhandled exceptions crashing your program. Guess what? That's a good thing! Your program has failed to operate as designed and cannot handle the error. What other choice do you really have? I write medical software and I can tell you that a program crash is preferred (not desired, just preferred) to reporting the wrong information to the doctor. A crash results in a service call. Reporting the wrong data results in a regulatory action from the FDA.

The ugly argument is even more amusing but also easier to understand. Bracketing your code with try/catch/finally statements does interrupt the flow of your code visually. And it requires more typing which for far too many people in this business is still a chore because they don't touch type. Still, once they start dealing with methods that raise exceptions, they grudgingly give in because there is no other choice for handling exceptions. End of argument, almost.

For some reason, I run into code every day where the parameters are not validated. Isn't this like programming 101? Check your inputs and never trust that they are valid until proven. One programmer told me he doesn't do it because of the ugliness factor. Really.

I have to admit that parameter checking is tedious and can munge-up the code a bit. For example:

public void SomeMethod(string name, object value)
{
  if (string.IsNullOrEmpty(name))
  {
    if (name == null)
      throw new ArgumentNullException("name");
    else
      throw new ArgumentException("name");
  }

  if (value == null)
    throw new ArguementNullException("value");

  // code here...
}

Checking strings is especially tedious because usually null or empty is invalid. For a long time, I coded it up like below which is a bit more compact.

public void SomeMethod(string name, object value)
{
  if (string.IsNullOrEmpty(name))
  {
    throw (name == null)
      ? new ArgumentNullException("name")
      : new ArgumentException("name")
  }

  if (value == null)
    throw new ArgumentNullException("value");
}

Not much better but it does compact down to fewer lines if you put the throw () ?: statement on the same line.

For my own code, I try to check every parameter every time and yes I find all this typing tedious and it does make the code noisy (if not ugly). To combat this problem, I wrote some static methods that handle most of the parameter checking I normally do. They reduce parameter checking to a single line of code. Here's an example:

public void SomeMethod(string name, object value)
{
  Throw.IfNullOrEmpty(name, "name");
  Throw.IfNull(value, "value");

  // code here
}

Nothing magically here. Throw is a static class with methods that encapsulate the error handling code. The name of the argument is passed in for better error reporting but is optional. For the truly lazy, you could do the following:

public void SomeMethod(string name, object value)
{
  Throw.IfNullOrEmpty(name);
  Throw.IfNull(value);

  // code here
}

I think anyone reading the above code for the first time would readily understand what Throw.IfNull() means. Ok, so less typing, clearer meaning and consistent reporting. Not a bad deal.

The Throw class has the following static methods which should be self-explanatory.

static public void IfNull(T value)
static public void IfNull(T value, string name)
static public void IfNullOrEmpty(string value)
static public void IfNullOrEmpty(string value, string name)
static public void IfNotEqual(T value, T compareTo) where T : IComparable
static public void IfNotEqual(T value, T compareTo, string name) where T : IComparable
static public void IfLessThan(T value, T max) where T : IComparable
static public void IfLessThan(T value, T max, string name) where T : IComparable
static public void IfLessThanOrEqual(T value, T max) where T : IComparable
static public void IfLessThanOrEqual(T value, T max, string name) where T : IComparable
static public void IfGreaterThan(T value, T min) where T : IComparable
static public void IfGreaterThan(T value, T min, string name) where T : IComparable
static public void IfGreaterThanOrEqual(T value, T min) where T : IComparable
static public void IfGreaterThanOrEqual(T value, T min, string name) where T : IComparable
static public void IfOutOfRangeInclusive(T value, T min, T max) where T : IComparable
static public void IfOutOfRangeInclusive(T value, T min, T max, string name) where T : IComparable
static public void IfOutOfRangeExclusive(T value, T min, T max) where T : IComparable
static public void IfOutOfRangeExclusive(T value, T min, T max, string name) where T : IComparable

It's worth spending just a few moments on the Throw class itself to discuss some of the issues I encountered. Lets just look at the IfNull methods first.

static class Throw
{
    const string NULL = "NULL";

    static public void IfNull(T value)
    {
        Throw.IfNull(value, null);
    }

    static public void IfNull(T value, string name)
    {
        if (value == null)
        {
            throw new ArgumentNullException(name ?? NULL);
        }
    }
}

First notice that although the class is not generic, the methods are generic. C# allows you to use generic methods independent of whether the class is generic. Very handy in this instance. But why use generics here? After all, you can compare "null" with any CLR object. The answer is boxing.

Boxing is what the compiler does when it passes a value type to a method that requires a reference type. Essentially, the compiler "wraps" the value type in a object. There's some overhead to doing this. By using generics, the overhead problem of boxing is eliminated. The same goes for all the other methods in this class.

But wait, how can value types be null? In general they can't unless you're using something called a Nullable ype. This was introduced in .Net 2.0 and in C# using the weird "int? x = 1" syntax.

While you won't check if an int is null, you should do range checking to see if the value is what is expected. The range checking classes of course check if their parameters are null so again to avoid boxing, the IfNull() methods are generic.

Another favorite of mine you might not be familiar with is the null coalescing operator (??). The ?? operator returns the left-hand operand if it is not null, or else it returns the right-hand operand. Makes for a nice concise syntax.

The code and numerous unit tests are available from the downloads page.

image

I built this using VS 2008 but as far as know you should be able to include this class in a VS 2005 project.

 

Comments

RE: To Throw or not to Throw, that is the question
by Mike
Thursday, June 26, 2008 11:19 PM

Gee, I thought someone might disagree :)


powered by Bloget™