71

I am having trouble figuring out how to break out of a loop that contains a switch statement. Break breaks out of the switch, not the loop.

There is probably a more elegant solution to this. I have implemented a flag that starts out as true and gets set to false and ends the loop. Can you offer a better solution?

Background: this code is used in a bar code workflow system. We have PocketPCs that have bar code scanners built in. This code is used in one of those functions. It prompts the user for different pieces of data throughout the routine. This piece allows them to scroll through some inventory records displaying that info on the PocketPC terminal (paged results) and allows them to enter "D" for Done, "Q" to quit.

Here is the current C# example that needs to be improved:

do
{
    switch (MLTWatcherTCPIP.Get().ToUpper())
    {
        case "": //scroll/display next inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "P": //scroll/display previous inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "D": //DONE (exit out of this Do Loop)
            // break; // this breaks out of the switch, not the loop
            // return; // this exists entire method; not what I'm after
            keepOnLooping = false;
            break;
        case "Q": //QUIT (exit out to main menu)
            return;
        default:
            break;
    }
} while (keepOnLooping);

Here is an example of code that does this in VB.NET

Do
    Select Case MLTWatcherTCPIP.Get().ToUpper
        Case "" ''#scroll/display next inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown()
        Case "P" ''#scroll/display previous inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextUp()
        Case "D" ''#DONE (exit out of this Do Loop)
            Exit Do
        Case "Q" ''#QUIT (exit out to main menu)
            Return
    End Select
Loop

Thanks,

nawfal
  • 70,104
  • 56
  • 326
  • 368
joshblair
  • 877
  • 1
  • 6
  • 14
  • 8
    This looks fine to me, a flag variable is a standard way to check a loop condition. – Ron Warholic Dec 31 '09 at 22:53
  • In Java (and some others), labeling the loop and using a [labelled break](http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html) would be the most straightforward answer. – Roy Tinker Jul 28 '16 at 22:13

15 Answers15

57

I'd try to avoid it, but you could use...

goto

However, angry mobs with pitchforks become an occupational hazard if you choose to do so.

Pang
  • 9,564
  • 146
  • 81
  • 122
Kevin Montrose
  • 22,191
  • 9
  • 88
  • 137
  • 3
    More than 15 years of experience tell me that sometimes goto is the best, fast and optimized option, keep the algorithm in one small place: keep it fast, efficient, atomic, without destroying it in a thousand pieces, reduce unnecessary calls. Examples, acoustic wave analysis, machine vision, real-time, etc. A flag will not make it slower, I see it as impossible, but many flags can be a reading problem. – Cheva Mar 30 '20 at 00:28
55

I find this form to be ever-so-slightly more readable:

bool done = false;
while (!done) 
{ 
    switch (MLTWatcherTCPIP.Get().ToUpper()) 
    { 
        case "": //scroll/display next inventory location 
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
            break; 
        case "P": //scroll/display previous inventory location 
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
            break; 
        case "D": //DONE (exit out of this Do Loop) 
            done = true;
            break; 
        case "Q": //QUIT (exit out to main menu) 
            return; 
        default: 
            break; 
    } 
}
Jeffrey L Whitledge
  • 58,241
  • 9
  • 71
  • 99
31

One option here is to refactor this loop into a method ("extract method"), and use return.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
12

The only other way I know of is the dreaded goto. MSDN also says this.

However, I see no reason why you'd use it in this case. The way you have implemented works fine, and is more maintainable than a goto. I would keep what you have.

McAden
  • 13,714
  • 5
  • 37
  • 63
  • 13
    While `goto` may be dreaded, there are a small number of cases where it is actually useful and I believe this is one of them. – Steve Guidi Jan 01 '10 at 00:35
10

You must use a goto statement for multi level breaks. It appears to be the only 'clean' way in C#. Using a flag is also useful, but requires extra code if the loop has other predicaments for running.

http://msdn.microsoft.com/en-us/library/aa664756(VS.71).aspx

It may be interesting to note that some other non-c languages have multi level breaks by doing break levels; (Java is just as useless, though, as it uses a goto disguised as a continue.. :P)

Tor Valamo
  • 33,261
  • 11
  • 73
  • 81
9

You can't easily break out of the outer loop, but you can continue it.

If you reverse your logic then you get this. Note there is a break immediately after the switch statement to exit the loop.

This isn't very readable code in my opinion, and I think a flag is still best.

   do
         {
            switch (Console.ReadKey().KeyChar.ToString())
            {
                case "U":
                    Console.WriteLine("Scrolling up");
                    continue;

                case "J":
                    Console.WriteLine("Scrolling down");
                    continue;

                case "D": //DONE (exit out of this Do Loop)
                    break;

                case "Q": //QUIT (exit out to main menu)
                    return;

                default:
                    Console.WriteLine("Continuing");
                    continue;
            }

            break;

        } while (true);

        Console.WriteLine("Exited");
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
8

Why not wrap the switch into a method that returns a boolean to keep on looping? It would have the side benefit of making the code more readable. There's a reason someone wrote a paper saying we don't need goto statements after all ;)

do
{
    bool keepOnLooping = TryToKeepLooping();
} while (keepOnLooping);

private bool TryToKeepLooping()
{
    switch (MLTWatcherTCPIP.Get().ToUpper())
    {
        case "": //scroll/display next inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "P": //scroll/display previous inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "D": //DONE (exit out of this Do Loop)
            // break; // this breaks out of the switch, not the loop
            // return; // this exists entire method; not what I'm after
            return false;
        case "Q": //QUIT (exit out to main menu)
            return true;
        default:
            break;
    }

    return true;
}
Jeffrey Cameron
  • 9,975
  • 10
  • 45
  • 77
4

A flag is the standard way to do this. The only other way I know of is to use a goto.

John Knoeller
  • 33,512
  • 4
  • 61
  • 92
3

You can replace the switch statement with an if/else statement. No goto needed and the break statement leaves the loop:

do
{
  String c = MLTWatcherTCPIP.Get().ToUpper();

  if (c = "")
    MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
  else if (c = "P")
    MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextUp();
  else if (c = "D")
     break;
  else if (c = "Q")
    return;
  else
  {
    // Handle bad input here.
  }
} while (keepLooping)
Chris R. Timmons
  • 2,187
  • 1
  • 13
  • 11
  • 4
    While this is fine for most applications, there is an important difference between `switch` and `if/else` being that the compiler can usually optimize the "branching" operations by using a jump table, resulting in faster code. – Steve Guidi Jan 01 '10 at 00:42
  • 5
    Steve, the code here is waiting for user input. It's already sat there for billions of nanoseconds doing absolutely nothing. Whether it takes one or one hundred nanoseconds to work out what the character typed was seems completely irrelevant. Optimizations should be driven by empirical data about real world user concerns, not by armchair guesses about what the compiler might do. – Eric Lippert Jan 01 '10 at 16:21
  • 1
    I agree with Steve's argument but in certain simple situations changing your logic to if/else would do the trick. – Alex Mar 22 '12 at 16:33
1

Write something like:

case "Exit/Break" :
                  //Task to do
                    if(true)
                      break;

This break will not be associated with any case. It will belong to the while loop.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

IMO, this seems a perfectly fine way of breaking out of a while loop. It does what you expect with no side effects. I could think of doing

if(!keepOnLooping)
  break;

But that's not really any different in terms of execution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fry
  • 4,106
  • 9
  • 38
  • 51
1

Wrap it into a function and use a return statement to exit. How about that?

Hamish Grubijan
  • 10,562
  • 23
  • 99
  • 147
0

Another (not so great) alternative is to uniquely handle the case where you have to "break out of the loop" with an if straight away and move it out of the switch block. Not terribly elegant if the switch-case is very long:

do
{
    var expression = MLTWatcherTCPIP.Get().ToUpper();
    if (expression = "D") //DONE (exit out of this Do Loop)
    {   
        statement;
        break;
    }

    switch (expression)
    {
        case "": //scroll/display next inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "P": //scroll/display previous inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "Q": //QUIT (exit out to main menu)
            return;
        default:
            break;
    }
} while (true); //or whatever your condition is

You can also make the case itself a part of the condition of while loop considering you only have to break out of the loop and the computation of expression itself is trivial (like reading a variable).

do
{
    switch (expression)
    {
        case "": //scroll/display next inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "P": //scroll/display previous inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "Q": //QUIT (exit out to main menu)
            return;
        default:
            break;
    }
} while (condition && expression != "D");

Also if refactoring the entire thing into a new method (which is the most elegant solution to this) is unacceptable for some reason, then you can also rely on an anonymous delegate to do the same inside the existing method.

nawfal
  • 70,104
  • 56
  • 326
  • 368
0

You could change the switch statement to a for/foreach loop. Once the condition is met set "keepOnLooping" to false and then use break to get out of the loop. The rest should take care of itself.

Chuck Conway
  • 16,287
  • 11
  • 58
  • 101
-2

May or may not work but lamda why not give it a shot just for fun

while(  (expr) => (){
switch(expr){
case 1: dosomething; return true; 
case 2 : something;return true;
case exitloop:return false;}
});