tags:

views:

262

answers:

10

How do you break out of a foreach loop while within a switch block?

Normally, you use break but if you use a break within a switch block it will just get you out of a switch block and the foreach loop will continue execution:

foreach (var v in myCollection)
{
    switch (v.id)
    {
        case 1:
            if (true)
            {
                break;
            }
            break;
        case 2;
            break
    }
}

What I'm currently doing when I need to break out of the foreach while within the switch block is setting a bool value placed outside of the loop to true and checking the value of this bool every time the foreach is entered and before entering the switch block. Something like this:

bool exitLoop;
foreach (var v in myCollection)
{
    if (exitLoop) break;
    switch (v.id)
    {
        case 1:
            if (true)
            {
                exitLoop = true;
                break;
            }
            break;
        case 2;
            break
    }
}

This works but I keep thinking there must be a better way of doing this I am unaware of...

EDIT: Wonder why this was not implemented in .NET the really neat way it works in PHP as mentioned by @jon_darkstar?

$i = 0;
while (++$i) {
    switch ($i) {
    case 5:
        echo "At 5<br />\n";
        break 1;  /* Exit only the switch. */
    case 10:
        echo "At 10; quitting<br />\n";
        break 2;  /* Exit the switch and the while. */
    default:
        break;
    }
}
+5  A: 

Honestly? This is perhaps the only situation where it is completely valid and proper to use goto:

foreach (var v in myCollection) {
    switch (v.id) {
        case 1:
            if (true)
                // document why we're using goto
                goto finished;
            break;
        case 2;
            break
    }
}
finished: // document why I'm here
meagar
Why use goto? It is much better to extract method. Each time you use goto statement a kitten dies.
Andrew Bezzub
Why extract method when you can just goto? It's six in one, half dozen the other, really. If the loop is small enough to belong in a single function in the first place, it seems a bit odd to refactor it out solely to avoid a clean and clear goto.
siride
Because using goto causes pain. Your methods should as short as possible. And if they are you will never need goto statement.
Andrew Bezzub
@Andrew *miss-use* of goto causes pain, the same can be said for any and every language construct.
meagar
What pain does it cause? return and break do effectively the same thing except there is no explicit label, potentially making the code less readable. The anti-goto canard needs to die. It's fine to rail against it from the days before procedural programming when people would truly write spaghetti code with nothing but gotos. Now that we mostly use higher-level constructs, the occasional goto is not a problem at all. The anti-goto hysteria is nothing more than cargo cult programming, AFAIC.
siride
+6  A: 

You could extract your foreach cycle to the separate method and use return statement. Or you could do like this:

        foreach (object collectionElement in myCollection)
        {
            if (ProcessElementAndDetermineIfStop(collectionElement))
            {
                break;
            }
        }

        private bool ProcessElementAndDetermineIfStop(object collectionElement)
        {
            switch (v.id)
            {
                case 1:
                    return true; // break cycle.
                case 2;
                    return false; // do not break cycle.
            }
        }
Andrew Bezzub
+1, this is much cleaner
Dan Bryant
+1  A: 

Lame, I know, but that's all you can do about it.

You could always transform it into a while loop and add 'exitLoop' as a condition which must be met. Inside the switch, you can call continue to skip the rest of the current pass and since you would have set exitLoop to false, it'd exit much like break would do. Even though it's not exactly what you're asking, perhaps it's more elegant that way?

Neil
+3  A: 

There's always the option of restructuring your code so that you can return from the switch statement.

klausbyskov
+9  A: 

Your solution is pretty much the most common option in this case. That being said, I'd put your exit check at the end:

bool exitLoop;
foreach (var v in myCollection)
{
    switch (v.id)
    {
        case 1:
            if (true)
            {
                exitLoop = true;
            }
            break;
        case 2;
            break
    }

    // This saves an iteration of the foreach...
    if (exitLoop) break;
}

The other main option is to refactor your code, and pull the switch statement and foreach loop out into a separate method. You could then just return from inside the switch statement.

Reed Copsey
+1  A: 

Based on MSDN documentation on the break statement, it only allows to stop the top-most scope.

This case is one where you could use a goto statement to leave your foreach loop. If you don't want to use a goto statement, your solution seems to be the best one.

As a side note, you could improve your code by testing the exitLoop flag at the end of the iteration, saving the cost of one enumerator call.

Thibault Falise
Which I find quite unfortunate, especially since Java allows you to break out of multi-level loop/switch constructs with, e.g., "break 2" syntax.
siride
+11  A: 

The boolean is one way. Another is using labels and goto. I know folks consider goto to be a cardinal sin, but used judiciously (VERY judiciously), it can be useful. In this case, place a label just past the end of the foreach loop. When you want to exit the loop, simply goto that label. For example:

foreach(var v in myCollection) {
    switch(v.Id) {
        case 1:
            if(true) {
                goto end_foreach;
            }
            break;
        case 2:
            break;
    }
}
end_foreach:
// ... code after the loop

EDIT: some people have mentioned taking the loop out into a separate method so that you can use return. I see the benefit of this as it doesn't require goto and it also simplifies the original function that contained the loop. However, if the loop is simple and is the primary purpose of the function that contains it, or if the loop makes use of out or ref variables, then it's probably best to just leave it in place and use the goto. In fact, because the goto and the label stand out, it probably makes the code clearer rather than clunkier. Putting it in a separate function could make simple code harder to read.

siride
Exactly. Well said.
TomTom
Do not use goto. It makes code more complicated. Extract method is much better.
Andrew Bezzub
It'd better if you'd state *why* goto makes code more complicated, especially when the alternative is just as complicated and moves code away from its original context.
siride
Putting additional functions never makes code reading harder if your functions are short and name is clear.
Andrew Bezzub
And so he'd have to name the labels differently for each loop. Not a very good long term solution to use goto.
Neil
I think having a thousand little functions obscures the big picture. I know it's all the rage in the hardcore OO circles, but I find spelunking through a bunch of functions whose existence is motivated purely by reasons like "never use goto" to be considerable more difficult than dealing with a single slightly longer, but coherent function.
siride
@Neil only if he has many such loops in one function. If that were the case, then the argument for breaking the function down into multiple subfunctions would make a lot more sense.
siride
@Andrew Making a short function which is only ever called from exactly one place for the sole purpose of being able to return mid-function is hardly a better solution than `goto`. You have yet to justify your anti-goto sentiments beyond regurgitating popular opinion.
meagar
@meagar. It is definitely better solution as long as function is atomic and you can understand what it does by just reading its name. Read http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882
Andrew Bezzub
@Andrew Because if a book says so, it must be true. Some of us have formed our own opinions based on critical thought and experience.
meagar
I'm pretty sure .NET architects would not include goto in the framework if they didn't feel there was a legitimate use for it and although this could be just such a case I'd still rather go with pulling the loop into a separate method as an alternative to using a flag...
kzen
@meagar Its not just the book, its Robert Martin. And of course I have my own opinion - goto should never be used.
Andrew Bezzub
@Andrew if the opinion isn't based on anything, then it's invalid. Give some reasons why goto should never be used (other than "I read a book that said it shouldn't be used").
siride
@siride The book says nothing about goto. It says about writing clear code that is easy to maintain and understand. I just don't see any case when goto is easier to understand when you are reding code.
Andrew Bezzub
+2  A: 

It's not really different from your exitLoop flag, but it might be more readable if you extract a method...

foreach (var v in myCollection)
{
    if(!DoStuffAndContinue(v))
        break;
}


bool DoStuffAndContinue(MyType v)
{
    switch (v.id)
    {
        case 1:
            if (ShouldBreakOutOfLoop(v))
            {
                return false;
            }
            break;
        case 2;
            break;
    }
    return true;
}
dahlbyk
A: 

If you want to check if any id in myCollection is equal to 1, as per your second example why just don't use myCollection.Any(x => x.id == 1);
Maybe it wasn't what you wanted to do but maybe it gives another angle on the problem...

Gorgen
A: 

Some languages (i know PHP is one, not sure about others) allow you to specify how many control structures you'd like to break out of with

break n;
where 1 is implied if you just do break

break 2 would do what you describe, were it available in C#. I don't believe that's the case so your exit flag is probably the best solution.

jon_darkstar
I really like the way this was solved in PHP...
kzen
yeah, im not much for using break but it is pretty cool
jon_darkstar