4

I'm looking for a keyword or clean way to exit a finally block without using selection statements. Consider the following example:

private bool AtteptToDoSomething()
{
    try
    {
        MethodThatCouldFail();
        return true;
    }
    catch
    {
        return false;
    }
    finally
    {
        //Jump out of function here?            

        //... to avoid executing this under certain conditions
        DoSomethingFinalizing();
    }
}

Attempts that didn't work:

finally
{
    return;
    break;
    continue;
    goto ExitSymbol;
    //This works, but would require a second try-catch block
    throw new Exception();
}

The compiler errors for above examples include:

Control cannot leave the body of a finally clause

and

Cannot jump out of the finally block

It's pretty clear now that a finally block can't transfer control by any regular means.

There is a pretty good explanation why this won't work with continue, but unfortunately not why this can't be done in any other case.

Are there any (CLR or C# based) reasons why this is not allowed/possible?

Are there any exceptions to this restriction except for throw?

Community
  • 1
  • 1
Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
  • 4
    The clean way would be to structure your code so you don't need jump ship. – Dave Van den Eynde Apr 14 '16 at 12:20
  • 2
    [This is not the solution your looking for](https://www.youtube.com/watch?v=532j-186xEQ). Seriously this sounds like an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – Liam Apr 14 '16 at 12:20
  • Could it be, that I'm in the wrong stackexchange portal? Is there any "Why was the language designed in this way" - portal? – Manfred Radlwimmer Apr 14 '16 at 12:29
  • 2
    @ManfredRadlwimmer: no, you can write developers. On stackoverflow it's off-topic because it's opinion based. I could ask why it should be allowed, i don't see any reason. – Tim Schmelter Apr 14 '16 at 12:35

5 Answers5

14

You cannot.

It's by design and it's described in a C# specification: 8.10 The try statement.

It is a compile-time error for a break, continue, or goto statement to transfer control out of a finally block. When a break, continue, or goto statement occurs in a finally block, the target of the statement must be within the same finally block, or otherwise a compile-time error occurs.

It is a compile-time error for a return statement to occur in a finally block.

To make what you want you could do something like:

try {}
catch {}
finally
{
    Magic();
}

and then

  void Magic()
  {     
        if (x == y)
           return;
        else
           a = b;    
  }
Sebastian 506563
  • 6,980
  • 3
  • 31
  • 56
  • 3
    I know that's the current compiler behavior, but I can't think of any reasons why it shouldn't be possible to jump out of a finally block. – Manfred Radlwimmer Apr 14 '16 at 12:24
  • 1
    Because that's what they decided should happen....? Why is anything like it is in the spec. Write a letter to microsoft maybe...? – Liam Apr 14 '16 at 12:25
  • 3
    Sure, that would be one possibility - I just hoped someone on stackoverflow knows enough about CLR to know why this design decision was necessary. – Manfred Radlwimmer Apr 14 '16 at 12:27
  • 1
    @ManfredRadlwimmer Imagine you are returning return 0 from catch and return 1 from finally. What will be returned? – Sebastian 506563 Apr 14 '16 at 12:27
  • I'm not trying to return a value from the finally block - I'm trying to jump out of it. I know the return value is already fixed at that point. – Manfred Radlwimmer Apr 14 '16 at 12:28
  • @ManfredRadlwimmer: the question is: why should it be possible if you can simply use an `if` or a method? – Tim Schmelter Apr 14 '16 at 12:33
  • @ManfredRadlwimmer only way to accomplish it is to use it as i have added in the answere or Dmitry Bychenko example – Sebastian 506563 Apr 14 '16 at 12:33
  • @ManfredRadlwimmer It's not just the return value that's already been determined, it's that subsequent execution has already been determined and jumping out of a finally clause essentially contradicts that. Imagine an exception has been thrown and you try to return a value instead. All these behaviors need to be described and that is part of the cost of developing a feature that, arguably, brings no benefit to the language. There were more important things for the design team to be working on. – Kyle Apr 14 '16 at 13:25
  • Not the answer I was hoping for, but still the best I got - thanks for the time you took to look this up, I'm going to accept this answer. – Manfred Radlwimmer Apr 15 '16 at 07:42
  • @ManfredRadlwimmer it just that all the statements in finally block needs to be executed before the 'return' statements execute in try/catch block. So basically, u can say that finally wants to go back to try/catch block before try+catch+finally block finishes. Hence it is not allowed to jump out of finally block – yogesh puttaswamy Aug 05 '20 at 04:27
3

Well, you can just not execute the block

...
finally
{
    if (!certain conditions) 
    {   
        //... to avoid executing this under certain conditions
        DoSomethingFinalizing();
    }
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • The question is not about the example provided above, it's about why can't I use jump statements that would exit a finally block. – Manfred Radlwimmer Apr 14 '16 at 12:30
  • 1
    Did nobody actually read the question? (ifs are selection statements) I know there are tons of ways to do this, I'm interested in why the compiler doesn't allow jump statements. – Manfred Radlwimmer Apr 14 '16 at 12:32
  • @ManfredRadlwimmer becouse of "return" example. There is always return at the end of block even if there is not visible. void have "return" too. – Sebastian 506563 Apr 14 '16 at 12:39
  • @ManfredRadlwimmer yes, we read the question. And it starts with "I'm looking for a keyword or clean way to exit a finally block". You can't blame people for trying to help you with the intention of not running some of the code in a finally block after you've written that. – Richard Irons Apr 14 '16 at 13:58
2

The C# specification indicates the following:

  • a finally block cannot contain a break, goto, or continue that would transfer control outside the finally block.
  • a finally block cannot contain a return statement.

In short, a finally block must execute to it's last line of code.

Source: https://msdn.microsoft.com/en-us/library/aa664733(v=vs.71).aspx

Also, according to the C# spec, a finally block is always executed after the try block or catch block are executed.

So, if these requirements were not in place, then there could be control flow conflicts with flow statements that occur with the try or catch blocks.

Example 1

try
{
  // do something
  continue;
}
finally
{
  break;
}

In the above case, the try block is indicating that flow should go to the top of the encompassing loop. But the finally block is indicating that the loop should be exited.

Which to do?

Example 2

try
{
  return 0;
}
finally
{
  return 1;
}

Here we have conflicting return values.

Which to return?

The writers of the C# specification could have chosen that the finally block's control flow overrides that of the try or catch block. But that would have complicated things with regard to the compiler and debugging.

Instead, they decided to keep it simple and avoid it completely by forbidding flow control out of the finally block.

Matt Houser
  • 33,983
  • 6
  • 70
  • 88
0

I know this is an old question but I stumbled upon the same situation but in PHP. I wasn't able to jump out of a finally block

PHP Fatal error: jump out of a finally block is disallowed

And because you and I my friend are curious souls in the realm of computer science, I posted this answer hoping that it will satisfy your curiosity.

Why it's not possible to jump out of a finally block?

Simply put, because finally does not guarantee that the exception is handled.

Finally block will always be executed whether the exception is handled or not, so if you're trying to jump out of a finally block that means you want to do more, and doing more here is like you're trying to access or deal with something that is not guaranteed to be existed (so it doesn't make sense).

Rain
  • 3,416
  • 3
  • 24
  • 40
0

You could do it by placing your label at the end of the finally block, like so:

try
{
    ...
}
finally
{
    if (condition) goto ExitFinally;
    ...
    ExitFinally:
}
Tim
  • 91
  • 9