108

I don't have a problem; I'm just curious. Imagine the following scenario:

foreach (var foo in list)
{
    try
    {
         //Some code
    }
    catch (Exception)
    {
        //Some more code
    }
    finally
    {
        continue;
    }
}

This won't compile, as it raises compiler error CS0157:

Control cannot leave the body of a finally clause

Why?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lpaloub
  • 868
  • 2
  • 8
  • 21
  • 7
    So. Just curious. If it makes complete sense to you why it won't compile, why do you want someone to explain what already makes sense? =) – J. Steen Aug 01 '13 at 10:10
  • 14
    why would you need a `continue;` in `finally` block? isn't it same as `continue;` after the `try -- catch` block? – bansi Aug 01 '13 at 10:10
  • Probably because its pointless, finally is the last thing that will happen – Sayse Aug 01 '13 at 10:10
  • Is there a typo in the sample (`do`)? – Dennis Aug 01 '13 at 10:11
  • 6
    @J.Steen No, I DO know that the `finally/continue` is a limitation of the C# compiler :-) I'm curious too on the reason of this limitation. – xanatos Aug 01 '13 at 10:14
  • bansi, yes that's exactly what I do to solve the situation. I was just surprised it wasn't working – lpaloub Aug 01 '13 at 10:15
  • @xanatos I just meant the typo in the code. Usually it's recommended to leave it in, but since it was so *obviously* a typo that had nothing to do with the question, I changed my mind about leaving a comment about it. – J. Steen Aug 01 '13 at 10:15
  • J. Steen, CodeCaster edited and it makes sense now :P – lpaloub Aug 01 '13 at 10:16
  • Good. ;) Clarity is key! – J. Steen Aug 01 '13 at 10:19
  • @bansi, lpaloub: no, it is not the same at all. A continue; after the try does not run if you get an uncaught exception. – R. Martinho Fernandes Aug 01 '13 at 10:19
  • 5
    @xanatos - Technically speaking, it's a limitation of the underlying CIL. From the [language spec](http://www.google.com/patents/US6560774): "Control transfer is never permitted to enter a catch handler or finally clause except through the exception handling mechanism." and "Control transfer out of a protected region is only permitted through an exception instruction (leave, end.filter, end.catch, or end.finally)." The `br` family of branch instructions cannot accomplish this. – Unsigned Aug 01 '13 at 20:04
  • 1
    @Unsigned: it is a good limitation too, allowing it would be bizarre :) – user7116 Aug 08 '13 at 17:27
  • continue in finally will be permitted from starting from Python 3.8 – bakytn Oct 14 '19 at 12:48

11 Answers11

152

finally blocks run whether an exception is thrown or not. If an exception is thrown, what the heck would continue do? You cannot continue execution of the loop, because an uncaught exception will transfer control to another function.

Even if no exception is thrown, finally will run when other control transfer statements inside the try/catch block run, like a return, for example, which brings the same problem.

In short, with the semantics of finally it doesn't make sense to allow transferring control from inside a finally block to the outside of it.

Supporting this with some alternative semantics would be more confusing than helpful, since there are simple workarounds that make the intended behaviour way clearer. So you get an error, and are forced to think properly about your problem. It's the general "throw you into the pit of success" idea that goes on in C#.

C#, you, and the out if success

If you want to ignore exceptions (more often than not is a bad idea) and continue executing the loop, use a catch all block:

foreach ( var in list )
{
    try{
        //some code
    }catch{
        continue;
    }
}

If you want to continue only when no uncaught exceptions are thrown, just put continue outside the try-block.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • 13
    I will accept this as the answer, because you make understand the reasons why Microsoft possibly decided to not accept a continue in a finally. Probably that images has convinced me too :) – lpaloub Aug 01 '13 at 11:11
  • 20
    can you elaborate the "throw you into the pit of success" idea? I didn't get it :-D – Ant Aug 01 '13 at 11:28
  • 9
    @Ant http://www.codinghorror.com/blog/2007/08/falling-into-the-pit-of-success.html – Josh Aug 01 '13 at 11:37
  • 13
    The image was made by Jon Skeet, btw. I got from here: http://msmvps.com/blogs/jon_skeet/archive/2010/09/02/presentation-preparation.aspx – R. Martinho Fernandes Aug 01 '13 at 11:51
  • 1
    Of course, a `continue` in a `finally` block will be OK if it only continues a local loop defined inside the `finally` block. In the question, however, it tries to continue an "outer" loop. Similar statements that you can't have inside a `finally` block are `return`, `break` (when breaking out of the block) and `goto` (when going to a label outside the `finally` block). For a related Java discussion, see [Returning from a finally block in Java](http://stackoverflow.com/questions/48088/). – Jeppe Stig Nielsen Aug 01 '13 at 15:27
  • 1
    Nice answer. @lpaloub: Indeed, these are exactly the sorts of concerns the language design team considered when making restrictions like this. I note that there are similar restrictions involving the interactions of `yield` and `await` with `try` blocks, for similar reasons. – Eric Lippert Aug 01 '13 at 15:36
  • 1
    `You cannot continue execution of the loop, because an uncaught exception will transfer control to another function.` There it is. – Lightness Races in Orbit Aug 01 '13 at 15:37
  • -1 for an example showing unconditional catch of exceptions. Unless you code can safely and sensibly handle "windows has asked the program to exit","you were out of memory", and "stack overflow" (and other even more exotic exceptions; eg:classloader) exceptions you should NEVER unconditionally catch, except to log and throw. – Andrew Hill Apr 25 '19 at 23:43
32

Here is a reliable source:

A continue statement cannot exit a finally block (Section 8.10). When a continue statement occurs within a finally block, the target of the continue statement must be within the same finally block; otherwise, a compile-time error occurs.

It is taken from MSDN, 8.9.2 The continue statement.

The documentation say that:

The statements of a finally block are always executed when control leaves a try statement. This is true whether the control transfer occurs as a result of normal execution, as a result of executing a break, continue, goto, or return statement, or as a result of propagating an exception out of the try statement. If an exception is thrown during execution of a finally block, the exception is propagated to the next enclosing try statement. If another exception was in the process of being propagated, that exception is lost. The process of propagating an exception is discussed further in the description of the throw statement (Section 8.9.5).

It is from here 8.10 The try statement.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mortalus
  • 10,574
  • 11
  • 67
  • 117
31

You may think it makes sense, but it doesn't make sense actually.

foreach (var v in List)
{
    try
    {
        //Some code
    }
    catch (Exception)
    {
        //Some more code
        break; or return;
    }
    finally
    {
        continue;
    }
}

What do you intend to do a break or a continue when an exception is thrown? The C# compiler team doesn't want to make decision on their own by assuming break or continue. Instead, they decided to complain the developer situation will be ambiguous to transfer control from finally block.

So it is the job of developer to clearly state what he intends to do rather than compiler assuming something else.

I hope you understand why this doesn't compile!

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • On a related note, even if there were no "catch", the execution path following a `finally` statement would be affected by whether the `catch` had exited via exception, and there is no mechanism to say how that should interact with a `continue`. I like your example, though, because it shows an even bigger problem. – supercat Aug 02 '13 at 18:17
  • @supercat Agree with you, my answer shows an example of ambiguous situation for compiler still there are so many problems with this approach. – Sriram Sakthivel Aug 02 '13 at 19:51
16

As others have stated, but focused on exceptions, it's really about ambiguous handling of transferring control.

In your mind, you're probably thinking of a scenario like this:

public static object SafeMethod()
{
    foreach(var item in list)
    {
        try
        {
            try
            {
                //do something that won't transfer control outside
            }
            catch
            {
                //catch everything to not throw exceptions
            }
        }
        finally
        {
            if (someCondition)
                //no exception will be thrown, 
                //so theoretically this could work
                continue;
        }
    }

    return someValue;
}

Theoretically, you can track the control flow and say, yes, this is "ok". No exception is thrown, no control is transferred. But the C# language designers had other issues in mind.

The Thrown Exception

public static void Exception()
{
    try
    {
        foreach(var item in list)
        {
            try
            {
                throw new Exception("What now?");
            }
            finally
            {
                continue;
            }
        }
    }
    catch
    {
        //do I get hit?
    }
}

The Dreaded Goto

public static void Goto()
{
    foreach(var item in list)
    {
        try
        {
            goto pigsfly;
        }
        finally
        {
            continue;
        }
    }

    pigsfly:
}

The Return

public static object ReturnSomething()
{
    foreach(var item in list)
    {
        try
        {
            return item;
        }
        finally
        {
            continue;
        }
    }
}

The Breakup

public static void Break()
{
    foreach(var item in list)
    {
        try
        {
            break;
        }
        finally
        {
            continue;
        }
    }
}

So in conclusion, yes, while there is a slight possibility of using a continue in situations where control isn't being transferred, but a good deal (majority?) of cases involve exceptions or return blocks. The language designers felt this would be too ambiguous and (likely) impossible to ensure at compile time that your continue is used only in cases where control flow is not being transferred.

Chris Sinclair
  • 22,858
  • 3
  • 52
  • 93
  • Got the same situation in java when proguard was crashing while obfuscation. Your explanation is pretty good. C# gives compile time error - perfectly makes sense. – Dev_Vikram Mar 09 '17 at 10:51
11

In general continue does not make sense when used in finally block. Take a look at this:

foreach (var item in list)
{
    try
    {
        throw new Exception();
    }
    finally{
        //doesn't make sense as we are after exception
        continue;
    }
}
ghord
  • 13,260
  • 6
  • 44
  • 69
  • "In general" a lot of things don't make sense. And it doesn't mean it shouldn't work "in particular". IE: `continue` makes no sense outside loop statement, but it doesn't mean it's not supported. – zerkms Aug 01 '13 at 10:15
  • I think it make some sense. `item` can be file, read failed -> `finally` closes the file. `continue` prevents rest of the processing. – jnovacho Aug 01 '13 at 10:15
5

"This won't compile and I think it makes complete sense"

Well, I think it doesn't.

When you literally have catch(Exception) then you don't need the finally (and probably not even the continue).

When you have the more realistic catch(SomeException), what should happen when an exception is not caught? Your continue wants to go one way, the exception handling another.

H H
  • 263,252
  • 30
  • 330
  • 514
  • 2
    I think you could need `finally` when you have `catch`. It's commonly used for closing resources in reliable way. – jnovacho Aug 01 '13 at 10:19
  • It's not just exceptions though. You could _easily_ have a `return` statement within your `try` or `catch` blocks. What do we do now? Return or continue the loop? (and if we do continue, what if it's the last item to iterate? Then we just continue outside the `foreach` and the return never happens?) EDIT: Or even `goto` to a label outside the loop, pretty much _any_ action that transfers control outside the `foreach` loop. – Chris Sinclair Aug 01 '13 at 10:21
  • 2
    I don't agree with "When you literally have catch(Exception) then you don't need the finally (and probably not even the continue).". What if I need to do an operation whatever an exception has raised or not? – lpaloub Aug 01 '13 at 10:30
  • OK, the finally caters for additional `return`s inside the block. But normally execution continues after the catch-all. – H H Aug 01 '13 at 11:08
  • 'finally' is not 'catch'. It should be used for cleanup code. a finally block **runs regardless whether an exception is thrown or not**. Stuff like closing files, or freeing memory (if you're using unmanaged code) etc. these are the things you put in a finally block – Robotnik Aug 01 '13 at 11:46
  • My turn. @Henk - and these boots are made for walking. I bring up the finally part because you say it's not needed if you have a catch-all. – Robotnik Aug 01 '13 at 11:56
3

You cannot leave the body of a finally block. This includes break, return and in your case continue keywords.

Joseph Devlin
  • 1,754
  • 1
  • 26
  • 37
3

The finally block can be executed with an exception waiting to be rethrown. It wouldn't really make sense to be able to exit the block (by a continue or anything else) without rethrowing the exception.

If you want to continue your loop whatever happens, you do not need the finally statement: Just catch the exception and don't rethrow.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Falanwe
  • 4,636
  • 22
  • 37
1

finally runs whether or not an uncaught exception is thrown. Others have already explained why this makes continue illogical, but here is an alternative that follows the spirit of what this code appears to be asking for. Basically, finally { continue; } is saying:

  1. When there are caught exceptions, continue
  2. When there are uncaught exceptions, allow them to be thrown, but still continue

(1) could be satisfied by placing continue at the end of each catch, and (2) could be satisfied by storing uncaught exceptions to be thrown later. You could write it like this:

var exceptions = new List<Exception>();
foreach (var foo in list) {
    try {
        // some code
    } catch (InvalidOperationException ex) {
        // handle specific exception
        continue;
    } catch (Exception ex) {
        exceptions.Add(ex);
        continue;
    }
    // some more code
}
if (exceptions.Any()) {
    throw new AggregateException(exceptions);
}

Actually, finally would have also executed in the third case, where there were no exceptions thrown at all, caught or uncaught. If that was desired, you could of course just place a single continue after the try-catch block instead of inside each catch.

nmclean
  • 7,564
  • 2
  • 28
  • 37
1

Technically speaking, it's a limitation of the underlying CIL. From the language spec:

Control transfer is never permitted to enter a catch handler or finally clause except through the exception handling mechanism.

and

Control transfer out of a protected region is only permitted through an exception instruction (leave, end.filter, end.catch, or end.finally)

On the doc page for the br instruction:

Control transfers into and out of try, catch, filter, and finally blocks cannot be performed by this instruction.

This last holds true for all branch instructions, including beq, brfalse, etc.

Unsigned
  • 9,640
  • 4
  • 43
  • 72
-1

The designers of the language simply didn't want to (or couldn't) reason about the semantics of a finally block being terminated by a control transfer.

One issue, or perhaps the key issue, is that the finally block gets executed as part of some non-local control transfer (exception processing). The target of that control transfer isn't the enclosing loop; the exception processing aborts the loop and continues unwinding further.

If we have a control transfer out of the finally cleanup block, then the original control transfer is being "hijacked". It gets canceled, and control goes elsewhere.

The semantics can be worked out. Other languages have it.

The designers of C# decided to simply disallow static, "goto-like" control transfers, thereby simplifying things somewhat.

However, even if you do that, it doesn't solve the question of what happens if a dynamic transfer is initiated from a finally: what if the finally block calls a function, and that function throws? The original exception processing is then "hijacked".

If you work out the semantics of this second form of hijacking, there is no reason to banish the first type. They are really the same thing: a control transfer is a control transfer, whether it the same lexical scope or not.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • The difference is that `finally` by definition *must* be immediately followed by continuing the transfer that triggered it (an uncaught exception, `return`, etc.). It would be easy to allow "goto-like" transfers within `finally` (which languages do this, by the way?), but doing so would mean that `try { return } finally { ... }` *may not return*, which is completely unexpected. If `finally` calls a function that throws, that's not really the same thing, because we already expect that exceptions may occur at any time and interrupt normal flow. – nmclean Aug 01 '13 at 21:43
  • @nmclean: Actually, an exception which escapes `finally` is sorta same thing, in that it can result in unexpected and illogical program flow. The only difference is that the language designers, being unable to reject all programs where a finally block could throw an unhandled exception, instead allow such programs to compile and hope the program can accept any consequences that may follow the abandonment of the earlier exception-unwinding sequence. – supercat Aug 02 '13 at 18:26
  • @supercat I agree that it breaks expected flow, but my point is that that is *always* the case for unhandled exceptions, whether the current flow is a `finally` or not. By the logic of Kaz's last paragraph, functions should also be free to affect the flow in their caller's scope by way of `continue` etc., because they are allowed to do so by way of exceptions. But this of course is not allowed; exceptions are the *sole* exception to this rule, hence the name "exception" (or "interrupt"). – nmclean Aug 02 '13 at 18:48
  • @nmclean: Non-nested exceptions represent a control flow which differs from normal execution, but is still structured. The statement following a block should only execute if all exceptions that occurred within that block were caught within it. That might seem like a reasonable expectation, but an exception which occurs within a `finally` block can violate it. Really a nasty situation, which IMHO should have been solved by at minimum letting `finally` blocks know about any pending exceptions so they can build composite exception objects. – supercat Aug 02 '13 at 19:04
  • @mmclean Sorry, I do not agree that the name "exception" comes from some "exception to the rule" (regarding control) specific to C#, LOL. The control flow altering aspect of an exception is simply a non-local dynamic control transfer. A regular control transfer within the same lexical scope (no unwinding taking place) can be regarded as a special case of that. – Kaz Aug 02 '13 at 21:20
  • @Kaz I don't know how you got the idea that I thought "exception" is specific to C#. My point is they are *exceptional* and by definition *interrupt* flow. As such they can't be pointed to as an example of how standard constructs should behave. – nmclean Aug 02 '13 at 21:29
  • @nmclean A goto, break or continue also interrupts flow. – Kaz Aug 02 '13 at 22:01
  • @Kaz I'm sure you understand that there is a significant difference between logical branching and exceptions which can interrupt the entire call stack. If we accept the argument you presented in the last paragraph of your answer, we have two choices: Either we define certain cases where exceptions are not allowed to be thrown, or we allow other transfers everywhere that exceptions are allowed. Either way is conceptually illogical. Branches form the intrinsic structure of a particular algorithm, and exceptions report unexpected failures to the outside world. – nmclean Aug 02 '13 at 23:52
  • @supercat I'm not sure you worded that right. As stated, a `throw` within `finally` doesn't break your rule that "the statement following the block should not execute", because it goes up the call stack, not to the next statement outside the block. I don't see what expectation is violated. Can you give an example of what you mean? – nmclean Aug 03 '13 at 00:01
  • @nmclean: Method One calls Two within a try that catches `FooException`. Two calls Three within a try that catches `BarException`. Three calls Four within a try whose finally block throws `BarException`. Four throws `FooException`. Nobody catches FooException, but One's call of Two completes normally. – supercat Aug 03 '13 at 03:07
  • @supercat I see the problem. Python 3 has a possible solution by attaching an exception which is currently being handled (by a catch or finally) to a new exception being thrown. This means that if you are logging exceptions, they would automatically include the full context, or if you are expecting this you could write code which explicitly re-raises a pending exception. Consider this code, which catches both `BarException` and `FooException`: http://ideone.com/siB1C6 – nmclean Aug 03 '13 at 15:16
  • Unfortunately, to properly handle this the code within the `finally` block would have to have a means of knowing what exception is pending, if any. While it would be possible for a language to add the CIL necessary to do make the information available to the application, neither VB.NET nor C# offers any particularly nice way of doing so. Further, the only way an interface associated with `using` (e.g. `IDisposable`) to make such information available to the called method is for it to be passed as a parameter. While it might often be good to have a `Dispose` method which will... – supercat Aug 04 '13 at 04:49
  • ...throw an exception if a certain problem occurs and no exception is pending, but refrain from throwing an exception of its own if it's called as a result of an exception in the guarded block, there's no way for a `Dispose` method to know whether any exception that might be pending in some execution context is associated with its guarded block. – supercat Aug 04 '13 at 04:51
  • @supercat I don't think it would be necessary for `Dispose` to be passed a parameter; it should be possible to provide a global function that is dynamic based on where it's called from, like [StackFrame](http://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.aspx) is. However, unless you need to know about pending exceptions even when there's no current exception, I don't see why the information needs to be directly available under a `finally` block. If it was available on exception objects, you could handle it with another try-catch around your `Dispose` implementation. – nmclean Aug 04 '13 at 19:53
  • @nmclean: A problem with using a global function is that if a `finally` block includes a `try/finally` block, it may be unclear whether the behavior of a `Dispose` should consider the success of the outer block or the inner one. In some cases, the outer would make more sense; in other cases, the inner. If `finally` and `Dispose` took exception parameters, the choice of parameter could indicate which block's success or failure was relevant. – supercat Aug 04 '13 at 21:30