Continue in nested while loops

63,529

Solution 1

UPDATE: This question was inspiration for my article on this subject. Thanks for the great question!


"continue" and "break" are nothing more than a pleasant syntax for a "goto". Apparently by giving them cute names and restricting their usages to particular control structures, they no longer draw the ire of the "all gotos are all bad all the time" crowd.

If what you want to do is a continue-to-outer, you could simply define a label at the top of the outer loop and then "goto" that label. If you felt that doing so did not impede the comprehensibility of the code, then that might be the most expedient solution.

However, I would take this as an opportunity to consider whether your control flow would benefit from some refactoring. Whenever I have conditional "break" and "continue" in nested loops, I consider refactoring.

Consider:

successfulCandidate = null;
foreach(var candidate in candidates)
{
  foreach(var criterion in criteria)
  {
    if (!candidate.Meets(criterion))
    {  // TODO: no point in continuing checking criteria.
       // TODO: Somehow "continue" outer loop to check next candidate
    }
  }
  successfulCandidate = candidate;
  break;
}
if (successfulCandidate != null) // do something

Two refactoring techniques:

First, extract the inner loop to a method:

foreach(var candidate in candidates)
{
  if (MeetsCriteria(candidate, criteria))
  { 
      successfulCandidate = candidate;
      break;
  }
}

Second, can all the loops be eliminated? If you are looping because you are trying to search for something, then refactor it into a query.

var results = from candidate in candidates 
              where criteria.All(criterion=>candidate.Meets(criterion))
              select candidate;
var successfulCandidate = results.FirstOrDefault();
if (successfulCandidate != null)
{
  do something with the candidate
}

If there are no loops then there is no need to break or continue!

Solution 2

    while
    {
       // outer loop

       while
       {
           // inner loop
           try
           {
               throw;
           }
           catch 
           {
               // how do I continue on the outer loop from here?
               goto REPEAT;
           }
       }
       // end of outer loop
REPEAT: 
       // some statement or ; 
    }

Problem solved. (what?? Why are you all giving me that dirty look?)

Solution 3

You can use a break; statement.

while
{
   while
   {
       try
       {
           throw;
       }
       catch 
       {
           break;
       }
   }
}

Continue is used to jump back to the top of the current loop.

If you need to break out more levels than that you will either have to add some kind of 'if' or use the dreaded/not recommended 'goto'.

Solution 4

Swap the try/catch structure with the inner while loop:

while {
  try {
    while {
      throw;
    }
  }
  catch {
    continue;
  }
}

Solution 5

No.
I suggest, extracting the inner loop into a separate method.

while
{
   // outer loop
       try
       {
           myMethodWithWhileLoopThatThrowsException()
       }
       catch 
       {
           // how do I continue on the outer loop from here?
           continue;
       }
   }
}
Share:
63,529
SkunkSpinner
Author by

SkunkSpinner

I love development.

Updated on November 16, 2020

Comments

  • SkunkSpinner
    SkunkSpinner over 3 years

    In this code sample, is there any way to continue on the outer loop from the catch block?

    while
    {
       // outer loop
    
       while
       {
           // inner loop
           try
           {
               throw;
           }
           catch 
           {
               // how do I continue on the outer loop from here?
               continue;
           }
       }
    }
    
  • Welbog
    Welbog almost 15 years
    The problem with this method is if there is extra work that needs to be done between the end of the inner loop and the end of the outer loop, it will be done when calling break, but wouldn't be done when calling continue. You'd need a flag if you need that code to not be executed. I'm not saying that this answer is wrong (heck, I upvoted it), I'm saying that it's deceptively simple.
  • zvrba
    zvrba almost 15 years
    This is problematic because the separate metod will not have access to existing local variables.
  • Welbog
    Welbog almost 15 years
    That's why Microsoft gave us function parameters.
  • Michael Meadows
    Michael Meadows almost 15 years
    +1 for "...extract the inner loop to a method." I require lot of justification in code reviews when I see nested loops. They usually hurt readability, maintainability, and stability. OP's question can be solved with a simple "return" or "throw" (thereby not relying on gotos in any way).
  • Michael Meadows
    Michael Meadows almost 15 years
    pass the variables as parameters, or if side-effects are necessary, send it through as an anonymous delegate to be executed in the method. Then the compiler will create a closure, preserving your local scope.
  • Michael Meadows
    Michael Meadows almost 15 years
    You hurt my feelings, using exceptions for no other purpose than flow control. No downvote, just hurt feelings. :(
  • Summer Sun
    Summer Sun almost 15 years
    This makes me want to vomit everywhere
  • Michael Meadows
    Michael Meadows almost 15 years
    That's a little more emphatic than "hurt my feelings."
  • Pavel Minaev
    Pavel Minaev almost 15 years
    Absolutely. When you think you need a goto, first stop for a moment and ponder if you really do. If you still need a goto, then just use it - it's in the language for a reason. It's not inherently evil either - it just commonly appears in evil patterns, and therefore should serve as a signal to stop and try to spot such patterns (and not to plunge into "OMG goto this is all wrong" panic).
  • Michael Meadows
    Michael Meadows almost 15 years
    Goto is not inherently evil, but it is a gateway drug to bad, lazy code. Of all of the ways to control flow, it's usually the worst.
  • Matthew Steven Monkan
    Matthew Steven Monkan almost 13 years
    Don't forget to add using System.Linq for the second refactoring technique.
  • Brian Leeming
    Brian Leeming over 12 years
    You also should not be using the exception handling process for normal code control flow
  • jeremy-george
    jeremy-george about 11 years
    gotos are bad all the time, unless you are a compilert or eric lipper
  • Mastenka
    Mastenka over 8 years
    I see what you did there
  • matpop
    matpop about 8 years
    May not compile unless you explicitly add an empty statement semicolon (REPEAT: ;)
  • Oliver Dixon
    Oliver Dixon over 7 years
    So verbose, where is continue {nameOfLoop} like Java.. :-(
  • Palec
    Palec over 6 years
    The second refactoring introduces the need for allocation (enumerables and enumerators implementing the query and its evaluation) and is generally slower than the loop as it does strictly more work. It may be negligible and outweighed by the gains in readability, but it may not. Good to keep that in mind.
  • sinedsem
    sinedsem almost 4 years
    Doesn't work if you have some code in outer loop after inner loop.
  • palota
    palota about 3 years
    That's why Microsoft gave us "local functions" (since C# 7 and Visual Studio 2017) which do not need any parameters because they have access to all local variables of outer functions.
  • Jim G.
    Jim G. about 3 years
  • Jim G.
    Jim G. about 3 years
  • AustinWBryan
    AustinWBryan about 2 years
    @palota You cant really fault a guy for not knowing a feature that comes out 8 years after he posted that comment..