Продолжить во вложенных циклах - PullRequest
49 голосов
/ 15 июля 2009

В этом примере кода есть ли способ продолжить внешний цикл из блока catch?

while
{
   // outer loop

   while
   {
       // inner loop
       try
       {
           throw;
       }
       catch 
       {
           // how do I continue on the outer loop from here?
           continue;
       }
   }
}

Ответы [ 10 ]

97 голосов
/ 15 июля 2009

ОБНОВЛЕНИЕ: Этот вопрос был вдохновением для моей статьи на эту тему. Спасибо за отличный вопрос!


«continue» и «break» - не более чем приятный синтаксис для «goto». По-видимому, давая им милые имена и ограничивая их использование определенными структурами контроля, они больше не вызывают гнев толпы «все готы все время плохие».

Если то, что вы хотите сделать, это продолжить до внешнего, вы могли бы просто определить метку в верхней части внешнего цикла и затем "перейти" к этой метке. Если вы считаете, что это не мешает пониманию кода, то это может быть наиболее целесообразным решением.

Однако я бы воспользовался этой возможностью, чтобы подумать, выиграет ли ваш поток управления от некоторого рефакторинга. Всякий раз, когда у меня есть условные «разрыв» и «продолжение» во вложенных циклах, я рассматриваю рефакторинг.

Рассмотрим:

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

Два метода рефакторинга:

Сначала извлеките внутренний цикл в метод:

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

Во-вторых, могут ли все петли быть устранены? Если вы зацикливаетесь, потому что пытаетесь что-то найти, то выполните рефакторинг в запрос.

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
}

Если петель нет, разрывать или продолжать не нужно!

29 голосов
/ 15 июля 2009
    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 ; 
    }

Проблема решена. (что ?? Почему вы все так грязно смотрите на меня?)

18 голосов
/ 15 июля 2009

Вы можете использовать перерыв; заявление.

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

Продолжить используется для возврата к вершине токовой петли.

Если вам нужно пробить больше уровней, чем вам, вам нужно будет либо добавить какое-то «если», либо использовать страшное / не рекомендуемое «Перейти».

10 голосов
/ 15 июля 2009

Замена структуры try / catch на внутренний цикл while:

while {
  try {
    while {
      throw;
    }
  }
  catch {
    continue;
  }
}
4 голосов
/ 15 июля 2009

номер
Я предлагаю, извлекая внутренний цикл в отдельный метод.

while
{
   // outer loop
       try
       {
           myMethodWithWhileLoopThatThrowsException()
       }
       catch 
       {
           // how do I continue on the outer loop from here?
           continue;
       }
   }
}
3 голосов
/ 15 июля 2009

Используйте break во внутреннем цикле.

1 голос
/ 15 июля 2009

Вы просто хотите оторваться от внутреннего, которое продолжит внешнее.

while
{
   // outer loop

   while
   {
       // inner loop
       try
       {
           throw;
       }
       catch 
       {
           // how do I continue on the outer loop from here?
           break;
       }
   }
}
0 голосов
/ 23 июля 2009
using System;

namespace Examples
{

    public class Continue : Exception { }
    public class Break : Exception { }

    public class NestedLoop
    {
        static public void ContinueOnParentLoopLevel()
        {
            while(true)
            try {
               // outer loop

               while(true)
               {
                   // inner loop

                   try
                   {
                       throw new Exception("Bali mu mamata");
                   }
                   catch (Exception)
                   {
                       // how do I continue on the outer loop from here?

                       throw new Continue();
                   }
               }
            } catch (Continue) {
                   continue;
            }
        } 
    }

}

}
0 голосов
/ 15 июля 2009

Используйте собственный тип исключения, например, MyException. Тогда:

while
{
   try {
   // outer loop
   while
   {
       // inner loop
       try
       {
           throw;
       }
       catch 
       {
           // how do I continue on the outer loop from here?
           throw MyException;
       }
   }
   } catch(MyException)
   { ; }
}

Это будет работать для продолжения и выхода из нескольких уровней вложенных операторов while. Извините за плохое форматирование;)

0 голосов
/ 15 июля 2009

Я думаю, что лучший способ сделать это - использовать оператор break . Прерывание завершает текущий цикл , а продолжает выполнение с того места, где оно заканчивается . В этом случае завершит внутренний цикл и вернется во внешний цикл while . Вот как будет выглядеть ваш код:

while
{
   // outer loop

   while
   {
       // inner loop
       try
       {
           throw;
       }
       catch 
       {
           // break jumps to outer loop, ends inner loop immediately.
           break; //THIS IS THE BREAK
       }
   }
}

Я верю, что это то, чего вы хотели достичь, верно? Спасибо!

...