It's another exceptional post
published: Wed, 6-Apr-2005 | updated: Thu, 27-Oct-2005
Several things have turned up at work in the past few days, all related to exceptions in .NET. Wow, this blog certainly seems to be turning into a blog about exceptions.
The first one was that, if you are going to create your own exception
class hierarchy, you should derive the root of it from
Exception
and not ApplicationException
. It
seems that, although ApplicationException
used to be the
recommended ancestor, and although it is used as so throughout MSDN's
documentation, it is no longer. See this
thread on Brad Abrams's blog for further information, but the
argument goes that exception hierarchies should be broad and shallow;
shallow as in not more than two or three classes deep. In essence you
should have one ancestor for your hierarchy and then one exception
class for every type of error that needs to be reported. The
recommendation is to not use error code properties within your
exception classes either, but to have one exception class per error
code (the archetypical bad boy in this respect is
SqlException
, of course). Putting
ApplicationException
as your ancestor adds a new level to
your exception hierarchy without giving you any benefit whatsoever.
The second thing that cropped up was a bald reference in MSDN's Design Guidelines for Class Libraries which read, "Use the common constructors [...] when creating exception classes." The common constructors are
public class XxxException : Exception { public XxxException() {... } public XxxException(string message) {... } public XxxException(string message, Exception inner) {... } public XxxException(SerializationInfo info, StreamingContext context) {...} }
But there was no reason why. For some reason, I went off on a complete
tangent trying to find arcane reasons for this (man, I must have been
tired after the clock change on Sunday, or something). You see, my
years of using Delphi had taught me that constructors were inherited,
so I assumed that a bare XxxException
class definition in
C# would have these constructors already defined and usable. In
Delphi, you define a new exception class hierarchy like this:
type MyRootException = class(Exception); MyNilParameterException = class(MyRootException); MyOutOfBoundsException = class(MyRootException); ...
Quick and simple. No messing. No code, per se.
Well, more fool me. I found out that constructors are never inherited in C#. The only constructors you get for a class are the ones you explicitly write for the class. If you don't write any constructors at all then the compiler creates one for you (the one with no parameters), whose implementation is to call the base class' similarly defined constructor (and if the base class doesn't have one then you'll get a compile error).
So, now it's obvious why you should define these constructors. If you don't, they won't be there, and users of your exception class will be expecting them.
And, geez, the errors that creep into your reasoning and your code when you switch from language to language...
The third exceptional item was a discussion we were having about catch blocks. Our code has way too many catch blocks, most of which just log the exception and rethrow it. This tends to means that if an exception is thrown deep in a call stack, every method in the call stack will catch the exception, log it, and then throw it. Our log gets full of lines reporting the same exception for each method as it unwinds the call stack. Some catch blocks don't even rethrow the exception after logging it.
I'm proposing this. We divide up the exceptions into two categories: those exceptions we can catch and actually do something about straight away -- let's call them the recoverable exceptions -- and those exceptions we don't know what to do about because they are totally unforeseen.
An example of a recoverable exception is this one. There's a couple of places where SQL Server might return a lock error as an exception. In that case, we can retry the call to SQL Server again to see if the lock has gone. Of course there is the proviso that we only try again twice, after the third failure we actually assume the issue in not recoverable and allow the exception to escape.
For the non-recoverable exceptions we just let them escape all the way up to the outer method in the call stack for the thread or the appdomain. There they will be logged. My recommendation then is to let them escape, that is rethrow them, or to shut the application down (maybe with some Watson-like "phone home" error reporting first) but it's a difficult decision.
In essence, for some of these unrecoverable exceptions we could recover to a certain extent (shut down the application's engine and then restart it) but we run into the possibility of repeating the situation that caused the exception (can you spell infinite loop?). This is most likely if the exception is thrown due to some internal issue (for example, bad data in a file that's getting transformed for the database).
If the exception was due to some external reason, say the link to the database server going down, we could recover from by going into a loop until the situation resolves itself, and then starting up the engine again.
This stuff isn't easy by all means and deciding which exception is which in the abstract is probably prone to failure. Possibly the best idea is to run the application and hack its environment and/or throw stuff at it until we get an exception. (Examples: unplug the machine from the network, or forcibly terminate a process it's using, or feed it very bad input, or scribble over its files, etc, etc.)
Work out where the exception took place, and why, and then either strengthen the code so that the exception won't get thrown (best idea), or add a catch block in some method to try and recover from the exception (not so good an idea).