Best Practices for Exception Management
Addendums to Scott Hanselman's postFellow RD Scott Hanselman wrote his thoughts on Good Exception Management Rules of Thumb here.
I agree with everything he said, and I'll add a few points. By the way, to get even more about best practices and exceptions, you should read Chapter 7 of Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, by Cwalina & Abrams.
Scott says:
If your functions are named well, using verbs (actions) and nouns (stuff to take action on) then throw an exception if your method can't do what it says it can.
For example, SaveBook(). If it can't save the book - it can't do what it promised - then throw an exception. That might be for a number of reasons.
This is the core guidance for the framework design: A method fulfills its contract, or it throws. Period.
Scott sez:
If you can, throw an exception that means something, and if there's an exception that already exists that matches what happened semantically, throw that.
Don't create a HanselmanException just because you're writing the Hanselman module unless you're adding data or valuable semantics to the type.
My addendum: First of all, you should always throw exceptions that mean something. One of the key benefits to using exceptions to report errors is that they can carry rich semantic information about the cause of the error. Throwing meaningless exceptions nullifies that benefit. Regarding the creation of new exceptions, the reason to create a new exception type is that the caller may want to differentiate that type of error in a catch block. For example, SaveBook() might fail because its state is invalid, or the persistent storage is unavailable. The recovery process is different, therefore the exception types should be different. In the case of an unavailable storage medium, there already exists a good exception. In the case of an invalid internal state, a new type might be warranted.
Scott sez:
If something horrible happens (something exceptional) then you need to decide if you can keep going.
Don't catch exceptions you can't do anything about. It's likely if you could do something about it, it wouldn't be exceptional, and you might consider calling TryParse, or File.Exists, or whatever it takes to prevent that exception.
My addendum: Three points here: 1) File.Exists and its kin are referred to as the Tester-Doer pattern. And, while it’s often a good idea, it can introduce race conditions: State can change between the Test and the Do. For File.Exists, it’s probably unlikely, but if you implement it yourself, be aware of that issue. 2) TryParse() and it’s related methods, such as Dictionary.TryGetValue, solve both the exception problem and the race condition problem associated with the Tester-Doer pattern. 3) I’ve found that pointing people at the tester-doer pattern and the try pattern makes it much more clear when to provide your own implementation of the tester-doer pattern, or the Try-Parse pattern: SomeDictionary.GetItem(…) throws an exception if it can’t return an item. But, provide a SomeDictionary.TryGetItem() as well.
Scott's full postI doubt I'd have said it better
Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET LibrariesThe reference for .NET best practices