Why is the execution order of internal "final" and external "when" replaced with C # 6.0?

I saw this example:

static void Main(string[] args) { Console.WriteLine("Start"); try { SomeOperation(); } catch (Exception) when (EvaluatesTo()) { Console.WriteLine("Catch"); } finally { Console.WriteLine("Outer Finally"); } } private static bool EvaluatesTo() { Console.WriteLine($"EvaluatesTo: {Flag}"); return true; } private static void SomeOperation() { try { Flag = true; throw new Exception("Boom"); } finally { Flag = false; Console.WriteLine("Inner Finally"); } } 

Which produces the following output:

 Start EvaluatesTo: True Inner Finally Catch Outer Finally 

This sounds strange to me, and I'm looking for a good explanation of this order to wrap it in my head. I expected the finally block to be executed before when :

 Start Inner Finally EvaluatesTo: True Catch Outer Finally 

The documentation states that this order of execution is correct, but it does not specify why this is done so and what exactly are the rules of the order of execution here.

+44
Aug 02 '16 at 12:26
source share
2 answers

You may have been taught that when handling exceptions, each method is considered separately. That is, since your internal method has a try...finally , any exception will first call finally , and then will look at try above. This is not true.

From the ECMA CLR specification (ECMA-335, I.12.4.2.5 Exception Handling Overview):

When an exception occurs, the CLI looks for an array for the first protected block, which

  • Protects the region, including the current instruction pointer, and
  • It is a catch handler block and
  • Whose filter wants to handle the exception

If no match is found in the current method, a search is made for the calling method, etc. If no match is found, the CLI will delete the stack trace and interrupt the program.

If a match is found, the CLI moves the stack back to the point it just found, but this time it calls the finally and fault handlers. Then it starts the corresponding exception handler.

As you can see, the behavior is 100% consistent with the specification.

  • Find a protected block - try in SomeOperation
  • Does it have a catch handler block? No.
  • Find the protected block in the calling method - try in Main
  • Does it have a catch handler block? Yes!
  • Can a filter handle an exception? The filter is evaluated (disclaimer: this does not mean that all filters in the protected unit will always be evaluated - it’s not a problem if the filter has no side effects, which it really should not, of course), and the result is yes.
  • Go back onto the stack and execute all finally and fault handlers
    • finally in SomeOperation

finally in Main not part of this, of course - it will be executed when execution exits the protected block, regardless of the exception.

EDIT:

Just for completeness - it has always been so. The only thing that has changed is that C # now supports exception filters, which allows you to monitor the execution order. VB.NET supports exception filtering from version 1.

+36
Aug 02 '16 at 12:39 on
source share
Block

A finally always executes whether an exception is thrown.

The finally block also does:

  • After the catch block completes
  • After the control leaves the try block due to a jump instruction (for example, return or goto)
  • After the try block completes

The only things that can defeat the final block are an endless cycle or process that sets off suddenly. The finally block helps add determinism to the program

-5
Aug 02 '16 at 12:38 on
source share



All Articles