44

I've seen 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 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 documentation 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.

Graham
  • 7,431
  • 18
  • 59
  • 84
Archeg
  • 8,364
  • 7
  • 43
  • 90
  • @RahulTripathi No, my question is about swapped execution order in C# 6.0, this question has a direct execution order in vanilla C# before 6.0 – Archeg Aug 02 '16 at 12:30
  • 3
    It is an inevitable consequence of how the *when* clause works. The CLR cannot figure out what finally blocks need to execute until *after* it has figured out what catch clause is going to handle the exception. Only then can it start unwinding the stack, executing finally blocks while doing so. The possibly unintentional side-effects of the when expression is why it was left out of the C# language until v6. – Hans Passant Aug 02 '16 at 12:46
  • @HansPassant Why, specifically, is this true: "The CLR cannot figure out what finally blocks need to execute until after it has figured out what catch clause is going to handle the exception."? – Tavian Barnes Aug 02 '16 at 17:06
  • It is like stepping on your bicycle and not know where you are going. You can't decide to make a left or right turn. – Hans Passant Aug 02 '16 at 17:19
  • This is a good reason to avoid side effects in `when` clauses. ;) – jpmc26 Aug 02 '16 at 20:57

2 Answers2

38

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 (ECMA-335, I.12.4.2.5 Overview of exception handling):

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 doesn't mean that all filters in the protected block will always be evaluated - no problem if the filter has no side-effects, which it really shouldn't, of course), 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.

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • Wow. It turns out I had no idea how finally works in C# in reality. Seems by upvotes, a lot of people did. Do you know any explanation of why C# team decided to implement it like that, and not how you described it in the first paragraph? – Archeg Aug 02 '16 at 12:43
  • @Archeg -- One thing I'm still struggling with is your contention that this somehow changed in C# 6.0...are you saying this is *not* how it behaved before that? – rory.ap Aug 02 '16 at 12:45
  • @Archeg Well, I guess you never worked with structured exception handling on Windows :) The gist of it is, this behaviour was available for free on Windows, and exception filters aren't supposed to cause side-effects, so why would you do it another way? – Luaan Aug 02 '16 at 12:45
  • @roryap It turns out it didn't. My knowledge of how `finally` works in C# was wrong – Archeg Aug 02 '16 at 12:46
  • @Luaan I didn't. In fact I find it amazing, that C# still have so many things to learn even if you work with this stuff for so many years and think you know almost everything... And then you find a basic stuff that turns out to work differently than you thought. Thanks for a great explanation! – Archeg Aug 02 '16 at 12:51
  • 1
    @Archeg Well, at some point, it becomes very useful to go through the whole specifications of CLR and C# - you'll learn a lot of things that aren't necessarily even distinguishable in run-of-the-mill coding (until something like this happens :)). And those specs are really well written and with lots of rationale included too, so they're also a good insight to "what were they thinking?" – Luaan Aug 02 '16 at 12:54
  • 1
    @Archeg - it's hardly C# or even .NET - Windows SEH also follows this two-pass process for locating handlers and *then* unwinding. – Damien_The_Unbeliever Aug 02 '16 at 14:10
  • 1
    I think I have some ideas why, but if you're going to mention the "not 100% guaranteed" part, you should elaborate or at least link to another question or some documentation. – jpmc26 Aug 02 '16 at 20:58
  • @jpmc26 Just the usual - there's no contractual requirement for the runtime to evaluate every single exception filter in the protected block. For example, the exception might not be of the type given in the `catch` clause, or a different handler might have already been selected. Nothing that will bother you if your filter has no side-effects, which it really shouldn't have :) – Luaan Aug 03 '16 at 08:25
-5

A finally block always executes whether or not an exception is thrown.

A finally block executes either:

  • After a catch block finishes
  • After control leaves the try block because of a jump statement (e.g., return or goto)
  • After the try block ends

The only things that can defeat a finally block are an infinite loop or the process sending abruptly. A finally block helps add determinism to a program

svick
  • 236,525
  • 50
  • 385
  • 514
Thennarasan
  • 698
  • 6
  • 11
  • 2
    I don't see how this answers the question. You provided some general explanation about `finally` blocks, when the questions is very specific and is actually about `when`. – svick Aug 02 '16 at 13:53