I started this blog entry planning to explain exactly what happens when an exception is thrown from, or otherwise escapes a finally block in your code. I planned to explain in painful detail exactly what the runtime does in all of the various cases, with the goal of concluding (with substantial proof, I might add) that a finally block that throws is pure evil.
I couldn't do it in the half-hour that I had budgeted for this blog entry. It wasn't possible. So I have decided to refactor this blog entry into a little bit of philosophy, a statement of an unfortunate fact, and an example or two that should prove the case just fine without every convoluted detail. Oh yes, and I will wrap up with some guidance.
Promised Philosophy -
I believe that exception handling is an excellent way of reporting method failure in Object Oriented code. For this reason, I agree with the overall design of the .NET Framework in standardizing on exceptions as its primary means of expressing method failure. However, to wholeheartedly embrace exceptions for logical failure handling, it must be safe to throw from within any part of your application. Likewise, the behavior when you throw must be well defined and easy to understand in all circumstances.
An Unfortunate Fact -
The CLR's behavior when an exception is thrown from within a finally clause is neither safe nor easy to understand in all circumstances. Technically it is well-defined.
So what's all the hullabaloo about? The problem is that throwing from a finally block means that more than one exception can become active at the same time. No, this doesn't crash the CLR, but it is exceedingly difficult to button-down-the-hatches and avoid chaos in your application when this happens. Allow me to share some examples with you.
Some examples -
using System;
class App {
public static void Main() {
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnExcept);
try {
FinallyMadness(5);
} catch (InvalidOperationException) { // One catch fits all!
Console.WriteLine("catch");
}
}
static void FinallyMadness(Int32 end) {
try {
Console.WriteLine("throw");
throw new InvalidOperationException();
} finally {
if (--end != 0) {
FinallyMadness(end);
}
}
}
static void OnExcept(Object sender, UnhandledExceptionEventArgs args) {
Console.Write("unhandled");
}
}
No, I won't make you compile and run the application. I will tell you what the output of this application is (on versions 1.0, 1.1, and 2.0 of the CLR). Here goes:
throw
throw
throw
throw
throw
catch
That's right, five exceptions are thrown, and only one catch handler is required to catch them all. Is that catch handler called five times? No. Is the unhandled exception handler activated? No. Why is this? The reason is that each time a throw occurs, the one catch is sufficient to avoid triggering the unhandled exception handler, and then when the finally executes, the next throw replaces the previously active exception. That's it. One active exception replaces another, and in the end we catch the lucky exception that made it to the top. Never mind the other four exceptions. We didn't really mean to throw them anyway, right?
OK, but these exceptions are of the same type, what happens with exceptions of different types? If they had been different types surely one of them would trip our unhandled exception handler right? Right, unless you are unlucky and there are other catch handlers in the callstack that just happen to match each thrown exception type. Catch this:
using System;
class App {
public static void Main() {
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnExcept);
try {
ThreeThrow();
} catch (NullReferenceException) {
Console.WriteLine("catch");
} catch (ArgumentException) {
Console.WriteLine("catch");
} catch (InvalidOperationException) {
Console.WriteLine("catch");
}
}
static void ThreeThrow() {
try {
Console.WriteLine("throw");
throw new NullReferenceException();
} finally {
try {
Console.WriteLine("throw");
throw new ArgumentException();
} finally {
Console.WriteLine("throw");
throw new InvalidOperationException();
}
}
}
static void OnExcept(Object sender, UnhandledExceptionEventArgs args) {
Console.Write("unhandled");
}
}
OK, what's the output of this application? You guessed it:
throw
throw
throw
catch
That's right, one catch handles three exceptions of different types. Yes, the other catch clauses are necessary to make this possible, and yet their code never executes. "Jason, did you say that this behavior is well-defined?" Yes I did, and believe it or not, it is. It is just unrealistic to remember and design around in any realistic fashion.
Guidance -
#1. Consider a finally block that throws on purpose to be a bug in your code, and change it.
#2. Do whatever you can to stick to the basics in your finally blocks and perform whatever state-testing you can to avoid exceptions being raised by the methods you call in finally blocks.
#2. Don't catch and swallow exceptions in your finally blocks to solve this problem! This is no better (read worse!) than the default behavior of the runtime.
#3. If you are up for high-maintenance coding, consider wrapping the logic in all of your finallys in a catch-all exception block, that exits your application on the spot in the case of an exception (after logging the facts, of course).
#4. Always register for an unhandled exception handler in your applications that logs the facts, and when you are in your development phase, test with your debugger set to break on throw rather then breaking on unhanded exceptions. Take advantage of the selective exception settings of your debugger to make this practical.