Consider this program:
using System;
static class Program {
static void Main(string[] args) {
try {
try { throw new A(); }
finally { throw new B(); }
}
catch (B) { }
Console.WriteLine("All done!");
}
}
class A : Exception { }
class B : Exception { }
Here, an exception of type A
is thrown for which there is no handler. In the finally
block, an exception of type B
is thrown for which there is a handler. Normally, exceptions thrown in finally
blocks win, but it's different for unhandled exceptions.
When debugging, the debugger stops execution when A
is thrown, and does not allow the finally
block to be executed.
When not debugging (running it stand-alone from a command prompt), a message is shown (printed, and a crash dialog) about an unhandled exception, but after that, "All done!" does get printed.
When adding a top-level exception handler that does nothing more than rethrow the caught exception, all is well: there are no unexpected messages, and "All done!" is printed.
I understand how this is happening: the determination of whether an exception has a handler happens before any finally
blocks get executed. This is generally desirable, and the current behaviour makes sense. finally
blocks should generally not be throwing exceptions anyway.
But this other Stack Overflow question cites the C# language specification and claims that the finally
block is required to override the A
exception. Reading the specification, I agree that that is exactly what it requires:
- In the current function member, each
try
statement that encloses the throw point is examined. For each statementS
, starting with the innermost try statement and ending with the outermost try statement, the following steps are evaluated:
- If the
try
block ofS
encloses the throw point and if S has one or morecatch
clauses, the catch clauses are examined [...]- Otherwise, if the
try
block or acatch
block ofS
encloses the throw point and ifS
has afinally
block, control is transferred to thefinally
block. If thefinally
block throws another exception, processing of the current exception is terminated. Otherwise, when control reaches the end point of thefinally
block, processing of the current exception is continued.- If an exception handler was not located in the current function invocation, the function invocation is terminated, and one of the following occurs:
- [...]
- If the exception processing terminates all function member invocations in the current thread, indicating that the thread has no handler for the exception, then the thread is itself terminated. The impact of such termination is implementation-defined.
An exception isn't considered unhandled, according to my reading of the spec, until after all function invocations have been terminated, and function invocations aren't terminated until the finally
handlers have executed.
Am I missing something here, or is Microsoft's implementation of C# inconsistent with their own specification?