Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are CIL 'fault' clauses different from 'catch' clauses in C#?

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:

  • 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:

  • catchcatch (FooException) { … }
  • filter — not available in C# (but in VB.NET as Catch FooException When booleanExpression)
  • finallyfinally { … }
  • faultcatch { … }

Experiment:

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.

Questions:

  1. If catch { … } really is a catch clause, then how are fault clauses different from catch clauses?
  2. Does the C# compiler ever output fault clauses at all?
like image 507
stakx - no longer contributing Avatar asked Aug 16 '12 13:08

stakx - no longer contributing


2 Answers

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:

  • catchcatch (FooException) { … }
  • filter — not available in C#1 (but in VB.NET as Catch FooException When booleanExpression)
  • finallyfinally { … }
  • faultcatch { … }

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.

like image 174
Damien_The_Unbeliever Avatar answered Oct 16 '22 06:10

Damien_The_Unbeliever


.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.

like image 28
Hans Passant Avatar answered Oct 16 '22 05:10

Hans Passant