# Friday, June 29, 2007

I've touched on this before, but I'd like to expound some personal theories (or preferences) on exception handling in C#.  There are plenty of things to debate here, but this is what I believe.

There are a few assumptions I make about code to which these exception handling rules apply, and I'll try to make those assumptions evident as I go along.

  • Method names are verbs.  I'm big on verbs and nouns in code (which, I recently learned, makes me a fan of "Domain Driven Design").
    • If my method is supposed to transfer money from one back account to another, it should be called Transfer
    • If my method can't fulfill it's contract (in the form of it's verb) it should throw an exception.  If Transfer can't transfer, it should throw an exception.  Period.
      • Don't worry about whether the error is really "exceptional".  If it was really exceptional, you wouldn't catch it (see below).
  • Catch only exceptions you know how to handle, and be specific.
    • Don't catch System.Exception.
      • If you don't know ahead of time what the exception is going to be, you don't know how to handle it, and you should send it up the stack.
      • Catching System.Exception means trying to deal with the really exceptional case, which by definition you can't plan for.
  • Write your own application specific Exception classes, and write lots of them
    • Make exceptions as specific as possible
      • if I'm writing Transfer, and there isn't enough money in the source account to fulfill the request, throw an InsufficientFundsException, not ArgumentException or ApplicationException, or InvalidOperationException.
    • Hide implementation specific exceptions by wrapping them in custom exceptions.
      • If you are writing a configuration system, and in your initialization routine you look for an XML config file, and that file isn't there, don't throw FileNotFoundException.  Wrap the FileNotFoundException in your own ConfigurationInitializationException.
        • The consumer of the configuration system doesn't care about your config file, they just care that you failed to initialize.
        • The ConfigurationInitializationException should be part of your contract to the outside world, and they should plan to catch that, not FileNotFoundException.
        • If you change the config system to initialize itself from the database instead of an XML file, and the database isn't there, your consumer doesn't have to worry about DbNotFoundException, just your custom exception according to your contract.
    • Make your custom exceptions part of your contract
      • In Java, you can make that compile-time checkable.
      • In .NET, you can include the exception in your XML documentation using the <exception> element.  It's not enforceable, but at least it's documented.
    • Make sure your custom exceptions are serializable and implement the right constructors
      • VS 2005 has a template that generates good exception classes (type exception->tab)
    • There may be some very specific reasons for catching System.Exception (but I don't really believe that)
      • If you must, never catch Exception without throwing something else.
        • If you don't care if your code failed, why did you write it?  That probably points to a design problem.
      • If you find yourself arguing for catching Exception, you probably have a design problem.
        • There are real reasons for crashing your application.  If nobody knows how to handle an exception, should you really keep going?  Or just take your lumps and quit?
        • If your application writes to the database, and the database isn't there, should you keep logging errors, or just exit the application?  (This depends on how transient the problem is, etc., and I'm probably ratholing here so you get it, right?)

YMMV, but there you go.

Discuss.

Friday, June 29, 2007 11:54:55 AM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  | 
Friday, June 29, 2007 2:46:47 PM (Pacific Daylight Time, UTC-07:00)
This is one of those things I've found has been so hard to get pounded into peoples heads when trying to teach them .NET. I once inherited an application where every method looked like:
try {
//do something
}catch(Exception ex) {
throw new Exception("Couldn't <insertmethodnamehere>");
}

Thanks for the stack trace a***hole.

I have found one exception to this though and that is asmx services. Since there's no global error handler i've had to trap / rethrow in order to log the failures.

This topic always bothers me though because I find it hard to decide what should be validation and what should be exceptions. You could make that case that Transfer should return false or even a Result class that contains success / failure and any error messages. I can't think of many users that would be happy if the application died and needed to be restarted everytime an InsufficientFundsException is thrown, so you do have to handle it (probably in your GUI) but then your exception has become a control structure which seems like a smell to me.

I once thought about using signatures like Transfer(from,to,onSuccessCallback,onFailureCallback) but never took it much farther than that. I think it was because I had recently seen how Erlang did it and liked the syntax.

I wouldn't be too jealous of the java guys, I've seen a lot of code like public void Transfer() throw <insert list of about 15 exceptions here>

Overall good post though, I'll definately be referring people to it as a reference.
Sunday, July 01, 2007 9:12:10 PM (Pacific Daylight Time, UTC-07:00)
It still surprises me that company’s dev teams don’t have standard exception handling procedures like the ones you have mentioned. Nice post.
Monday, July 02, 2007 1:37:25 AM (Pacific Daylight Time, UTC-07:00)
@Chris,

maybe I misunderstood you when you talked about asmx missing a global error handler. Can't you use the global.asax Request_Error handler?
Monday, July 02, 2007 9:32:25 AM (Pacific Daylight Time, UTC-07:00)
Chris,

the problem I have with something like Transfer returning false is that it puts us back to the old COM pattern of constantly checking HRESULTs. I'd rather my defensive code took the form of catching specific exceptions than 10 levels of nested if(S_OK){} blocks.
I agree that you don't want the application to fail on something like an InsufficientFundsException, which is why it's important to advertise those exceptions as part of the contract that the caller needs to know about.
Comments are closed.