Archeg Archeg - 4 months ago 11
C# Question

Why the execution order of inner finally and outer when swapped in C# 6.0

I've seen this example in stackoverflow docs:

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");
}
}


(C# 6.0 Features)

Which produces the next output:

Start
EvaluatesTo: True
Inner Finally
Catch
Outer Finally


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

Start
Inner Finally
EvaluatesTo: True
Catch
Outer Finally


The doc states that this execution order is correct, but it does not elaborate on why it is done like that and what exactly are the rules of the execution order here.

Answer

You might have been taught that when exception handling occurs, every method is considered separately. That is, since your inner method has a try...finally, any exception will first trigger the finally, and then it will "look" for a try higher up. This isn't true.

From the ECMA specification of CLR:

  • When an exception occurs, the CLI searches the array for the first protected block that
    • Protects a region including the current instruction pointer and
    • Is a catch handler block and
    • Whose filter wishes to handle the exception
  • If a match is not found in the current method, the calling method is searched, and so on. If no match is found the CLI will dump a stack trace and abort the program.
  • If a match is found, the CLI walks the stack back to the point just located, but this time calling the finally and fault handlers. It then starts the corresponding exception handler.

As you can see, the behaviour is 100% compliant with the specification.

  1. Look for a protected block - try in SomeOperation
  2. Does it have a catch handler block? No.
  3. Look for a protected block in the calling method - try in Main
  4. Does it have a catch handler block? Yes!
  5. Does the filter wish to handle the exception? The filter is evaluated (disclaimer: this isn't 100% percent guaranteed), and the result is yes.
  6. Walk the stack back and execute all finally and fault handlers
    1. finally in SomeOperation

The finally in Main isn't part of this, of course - it will execute when execution leaves the protected block, regardless of the exception.

EDIT:

Just for completeness - this has always been this way. The only thing that changed is that C# now supports exception filters, which allows you to observe the order of execution. VB.NET supported exception filters from version 1.