Exception class, return codes, and messages

I've had a couple of questions recently about how to architecture libraries with respect
to exception handling, specifically related to exception granualarity. Questions like:

- Is is okay to define an exception with a return code inside of it?

How many exception classes should I have?  

The first place to start is the Error
Raising and Handling Guidelines
from the Design
Guidelines for Class Libraries
. There is lots of useful information in the design
guidelines even if you aren't writing libraries. The guidelines do mention something
like "don't use return codes", but that's about it.

One of the high-value design points when designing exceptions is robustness. The robustness
of the customer's code depends directly on the way you structure your exception hierarchy.
Here's some code that I've seen. Assume that ApplicationException is the actual type
I'm catching, not the base type of a hierarchy:

 try
{
    // code here...
}
catch (ApplicationException e)
{
    if (e.Message.IndexOf("Database Connection Error"))
    {
         // recovery code here
    }
}

There are at least two problems with the code, one major, and one a bit more subtle.
Do you see them?

I'll start with the more subtle one first. If your exception messages are localized,
it's likely that the string matching will fail. This is a bad bug, because it only
shows up in the localized version in an error case, and your tests aren't very
likely to catch it.

The second bug is a whopper. If the exception that comes in isn't a
database connection error, it is swallowed by the catch block, never to be heard
from again. That's pretty much the worst thing that exception handling code
can do.

The problem is that the exception design has forced the user to look inside the
exception to decide what to do. This is bad, whether it be the message text,
a return code, or the type of an inner exception that the user is looking for. 

To prevent this, I'd recommend the following guideline:

-
Make sure that your user doesn't have to grovel inside of your exception classes
to find out whether to handle the exception or not.

One way to determine this is to ask yourself, "Self, would a user ever care about
the different cases where ApplicationException is thrown?" In some cases this
is obvious, but in other cases, it's not quite so clear. My recommendation is
that you err on the side of more granularity rather than less granularity.