According to the CLI standard (Partition IIA, chapter 19) and the MSDN reference page for the System.Reflection.ExceptionHandlingClauseOptions
enum, there are four different kinds of exception handler blocks:
Given these brief explanations (cited from the CLI Standard, btw.), these should map to C# as follows:
catch (FooException) { … }
Catch FooException When booleanExpression
)finally { … }
catch { … }
A simple experiment shows that this mapping is not what .NET's C# compiler really does:
// using System.Linq;
// using System.Reflection;
static bool IsCatchWithoutTypeSpecificationEmittedAsFaultClause()
{
try
{
return MethodBase
.GetCurrentMethod()
.GetMethodBody()
.ExceptionHandlingClauses
.Any(clause => clause.Flags == ExceptionHandlingClauseOptions.Fault);
}
catch // <-- this is what the above code is inspecting
{
throw;
}
}
This method returns false
. That is, catch { … }
has not been emitted as a fault clause.
A similar experiment shows that in fact, a catch clause was emitted (clause.Flags == ExceptionHandlingClauseOptions.Clause
), even though no exception type has been specified.
catch { … }
really is a catch clause, then how are fault clauses different from catch clauses?there are four different kinds of exception handler blocks:
- catch clauses: "Catch all objects of the specified type."
- filter clauses: "Enter handler only if filter succeeds."
- finally clauses: "Handle all exceptions and normal exit."
- fault clauses: "Handle all exceptions but not normal exit."
Given these brief explanations (cited from the CLI Standard, btw.), these should map to C# as follows:
- catch —
catch (FooException) { … }
- filter — not available in C#1 (but in VB.NET as
Catch FooException When booleanExpression
)- finally —
finally { … }
- fault —
catch { … }
It's that last line where you went wrong. Read the descriptions again. fault
and finally
are described practically identically. The difference between them is that finally
is always entered, whereas fault
is only entered if control leaves the try
via an exception. Note that this means that a catch
block may have already acted.
If you write this in C#:
try {
...
} catch (SpecificException ex) {
...
} catch {
...
}
Then there is no way that the third block will be entered if control leaves the try
via a SpecificException
. That's why catch {}
isn't a mapping for fault
.
1Since people keep mentioning this in the comments, a) This part of the answer is a quote from the original question and b) yes, when
clauses have subsequently been added to C#. It was however accurate at the time of asking and isn't what this answer focusses on answering.
.NET exceptions piggy-back onto the operating system's support for exceptions. Called Structured Exception Handling on Windows. Unix operating systems have something similar, signals.
A managed exception is a very specific case of an SEH exception. The exception code is 0xe0434f53. The last three hex pairs spell "COM", tells you something about the way .NET got started.
A program in general might have a stake at knowing when any exception is raised and handled, not just managed exceptions. You can see this back in the MSVC C++ compiler as well. A catch(...) clause only catches C++ exceptions. But if you compile with the /EHa option then it catches any exception. Including the really nasty stuff, processor exceptions like access violations.
The fault clause is the CLR's version of that, its associated block will execute for any operating system exception, not just managed ones. The C# and VB.NET languages do not support this, they only support exception handling for managed exceptions. But other languages may, I only know of the C++/CLI compiler emitting them. Done for example in its version of the using statement, called "stack semantics".
It does make sense that C++/CLI would support that, it is after all a language that strongly supports directly calling native code from managed code. But not for C# and VB.NET, they only ever run unmanaged code through the pinvoke marshaller or the COM interop layer in the CLR. Which already sets up a 'catch-them-all' handler that translates unmanaged exceptions into managed ones. Which is the mechanism by which you get a System.AccessViolationException.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With