Krzysztof Cwalina makes some good points in his blog entry on the ApplicationException class. His follow up entry is also very good, and provides some handy hints for error handling and custom exception creation in general.
However, there are a few things that I think could be said in a different (and perhaps clearer) way, and a few other points I think he missed, so I've decided to put this blog entry together so I throw my two cents worth in on the subject.
If you're looking for some best practices to use when writing error handling code, try this CodeProject article.
Exceptions and Performance, When to Throw an Exception
Firstly, a note on performance. Most of you have probably heard this many times, but some may not have, and it will have a bearing on some of my later comments.
It is generally agreed the overhead of a try statement is minimal and quick to execute, but that actual throwing of an exception (using the throw statement) is slow. How slow depends on various things such as the call stack size, number of active catch handlers and so on.
As a result, the general rule is don't throw exceptions to control code flow. For example, if you're writing a function the finds an item in a list, what do you do if the item isn't found ? Do you throw an exception or return null (or some other value that means not found) ?
Some purists will tell you that throwing an exception is the 'right' thing to do, because it can't be ignored like a null value, and a 'CantFindTheItemYoureLookingForException' is easier to diagnose than a 'NullReferenceException'. This is true, but you must also ask yourself;
- Is this really an exception condition ?
- Is my function documented, and if so, is it really a problem to return null instead of throwing an exception ?
- Will my function ever be called in a loop, or during time-sensitive operations (i.e events).
The third question is really the killer, if the answer to that is yes, then you shouldn't be using an exception (if you're coding in .NET). If it's no, you should still consider the other two questions before you decide whether or not an exception is appropriate.
One more thing on this point - if it's appropriate to throw an exception, do so. Don't avoid throwing exceptions because you don't like them, or because they're slow if performance is going to be a problem with an exception occurring in the code your working with (perhaps a function that runs once on startup etc).
When and How to use Custom Exceptions
Krzysztof Cwalina is correct when he says you shouldn't create your own custom exception just for the sake of having your own exception class. He's also correct to say it's ok to throw the existing exception classes provided by the .NET framework. What he doesn't explain (to my satisfaction) is why.
The basic point of having different exception classes is;
- To allow developers to control which exceptions they catch, by group or by specific exception.
- To provide exception specific information.
Note point #1. If you're writing a procedure that takes an argument which cannot be null, it is fine to throw System.ArgumentNullException because this allows a developer calling your function to specifically trap that single exception and deal with it separately to any other exception your code might throw. There is no need to create another exception class that means the same thing, all you would be doing is forcing the calling developer to learn about your own exception class that isn't providing any value.
On the other hand, if you're writing a function that really does throw an exception that isn't catered for by an existing exception class you musn't be afraid to create your own. Creating a NotAllowedOnPublicHolidaysException class that's thrown from your ScheduleMeeting function is great, because once again it allows the developer calling your function to trap just that exception and deal with it, independently of any other exceptions that might occur. If you throw the ArgumentException, then the developer might not know whether Monday 29th of February was invalid because it was a holiday or because it doesn't exist in that year (assuming you throw the same exception for both cases). In which case, they must resort to catching both exceptions and trying to peel apart the exception objects message text (assuming it's unique to the two different cases) to determine how to handle the exception.
This is also the same reason throwing new Exception() is also the wrong thing to do.
So DO use custom exceptions, but use system exceptions in preference if an appropriate one already exists.
ApplicationException - Is It Harmful ?
Krzysztof Cwalina says that ApplicationException isn't harmful, just useless. He's nearly right.
The reason ApplicationException is useless is because some of the system exceptions inherit from this class. The idea behind ApplicationException is that only custom exceptions (i.e non .NET exceptions) would inherit from it. This would allow developers to catch ApplicationException and be guaranteed they would only catch their own exceptions (or perhaps exceptions). Since some of the .NET exceptions inherit from this class, it's not longer possible to do this.
ApplicationException isn't harmful in that if you inherit from it in your custom exceptions, it doesn't cause any problems. What could be harmful is any code that tries to catch (ApplicationException).
The reason this is harmful is that it's likely the developer who wrote the code believed they would only catch their own custom exceptions, but this is not the case.
The truth is I had some code that suffered from this problem. The code in question tried to invoke a method using certain reflection techniques. I wanted to code it so if the invoke failed, an alternate (and simpler) invoke mechanism was used. I figured I could put a catch (ApplicationException) block in and rethrow the error, following by a catch (Exception) block that tried the simpler invoke. Now the code was ugly and I've re-written it since anyway, but the point is that my code failed in nasty ways because I did occasionally catch .NET errors that derived from ApplicationException but that shouldn't have been treated as one by my code.
So ApplicationException can only harm you if you're catching it, not deriving or throwing it. If you are catching it, you might want to revise your code.
I saw a post somewhere recently saying ApplicationException was useless because it didn't provide any functionality over and above the Exception class. This person had missed the point, remember, the purpose of ApplicationException was to allow a particular group of exceptions to be caught.
So What Can We Use Instead ?
If you need ApplicationException like behaviour, the answer is quite simple - create your own. I have my own UserCodeExceptionBase class. You can create your own, with your own name of course, but here's what I did;
- I made my class abstract (and put base in the name). This is because throwing new UserCodeException() would be about as useful as throwing new Exception(). The calling developer still can't catch specific errors. By making my class abstract, I've forced myself to be tidy from now on - any time I want to throw a UserCodeException I must either use an existing custom exception or make a new one that is specific to the situation I'm in. This isn't required, but you might want to consider it.
- UserCodeExceptionBase inherits from ApplicationException. This means that it should be compatible with any existing (but broken) code that catches ApplicationException. Since I might throw my exception from class libraries I build, and those libraries may be used by other developers who have incorrectly assumed ApplicationException works, I decided the compatibility was important.
Deep Exception Hierarchies
Another thing I wanted to comment on is 'deep exception hierarchies'. I've only just recently started hearing that you should have too many levels of inheritance in your exception classes (and this may be one reason NOT to inherit from ApplicationException in your own UserCodeException class). To be honest, I haven't yet had anyone explain why it's bad, but I strongly suspect it relates to performance.
Assuming this is the case, I would question how much of a problem it is. We've already covered the fact that throwing exceptions isn't fast, and you shouldn't do it unless you really, really have to. Throw and catching exceptions shouldn't be the norm (that's why they're called exceptions). So if you're not throwing them often, why does it matter if there's a couple of extra layers of inheritance and it takes a bit longer to process the exception ? Even if it takes another second, assuming your exception is being thrown as a last resort, and only being thrown once, the performance hit shouldn't be too bad.
I guess it depends on your exact situation, but I wouldn't panic if you have four or five levels in inheritance. At least not if you're using exceptions appropriately in the first place. Obviously, you shouldn't use more levels than you need though.
Visual Basic Isn't the Poorer Cousin
Ok, so VB6's error handling was dirty, ugly, and often poorly used. With VB.NET, Visual Basic 'grew up' and got structured exception handling like C#. You might think that's the end of the story, but it's not.
I turns out VB.NET has some very nice exception handling features that C# lacks.
One of these features is 'Exception Filters'. This basically lets you call a function that returns a boolean value, before a catch block executes. If the function returns false, the catch block is ignored the exception continues to be throw to the next most appropriate catch block, or ends up unhandled. Personally I haven't had a need for this, but it's interesting that VB.NET has explicit syntax for allowing this, while C# does not. Panopticoncentral.net does have a blog entry describing one use for this feature.
The second feature (and more useful as far as I'm concerned) is the Retry statement. The Retry statement can be called from a catch block, and effectively 'goes to' the associated try command again. This is useful when you're dealing with exceptions thrown from a device, and you want to allow the user to 'Abort, Retry, Ignore'. With out the Retry statement, you must either use the horribly 'goto' command/equivalent (if it's available in your language) or a loop where the execution breaks only when the code executes successfully or the user elects to abort. While both of these methods are functionally equivalent, they are not as nice to read.
Exceptions Hidden by the .NET Runtime
Sometimes exceptions can be hidden by the .NET runtime. One instance of this I've discovered is exceptions that are thrown during databinding.
If you bind a control to a property on an object, the property set will be called when the controls value is 'written' by the data binding engine. If during that call an exception is thrown, the control or databinding error appears to swallow the exception. You will not get an unhandled exception, nor can use use the events on the System.Windows.Forms.Application object to be notified of it.
If you want access to the error you need to connect an event handler to the BindingComplete event of the data binding. Your event handler will receive an
BindingCompleteEventArgs object that contains the exception that occurred.
The worst thing about this behaviour, is the control doesn't re-read the value of the property, so the controls contents are now inconsistent with the actual data. While this shouldn't ever occur because the validation events on the control should be used to prevent bad values being passed to the object, this possible requires validation code in two places, and requires the validation code in the event understands all the possible situations a the object may throw an exception for. If you have controls bound to properties that may throw exceptions, be very careful.
Catching Unhandled or Threading Exceptions
Applications can use some events (System.Windows.Forms.Application.ThreadException and AppDomain.UnhandledException) to catch unhandled or exceptions from other threads.
Note, these event handlers are really only useful for logging event information. They cannot be used to 'handle' the exception, you cannot prevent the application from unloading if the event was thrown on the main thread. You also do not have access to any non-global information, except for the exception itself. Oh, and if you're a newbie, please avoid the temptation to make all your application internals global so they can be managed from these events - it's bad design. Use these events for logging, or not at all.
It should also be noted this event only fires for exceptions occurring within the AppDomain the application was started in. Exceptions occurring in other AppDomains created by the application itself will not fire these events.
See this CodeProject article for more details.