2009-07-15 5 views
40

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

while 
{ 
    // outer loop 

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

Вложенные петли только приводят к отчаянию. –

ответ

87

ОБНОВЛЕНИЕ: Этот вопрос был источником вдохновения для my article on this subject. Спасибо за отличный вопрос!


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

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

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

Рассмотрим:

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 
} 

Если нет петель, нет необходимости перерываться или продолжаться!

+5

+1 для «... извлечения внутреннего цикла в метод». Мне нужно много оправдания в обзорах кода, когда я вижу вложенные циклы. Они обычно ухудшают читаемость, ремонтопригодность и стабильность. Вопрос OP может быть решен с помощью простого «возврата» или «броска» (тем самым никак не полагаясь на gotos). –

+6

Абсолютно. Когда вы думаете, что вам нужно «перейти», сначала остановитесь и подумайте, действительно ли вы это делаете. Если вам по-прежнему нужен 'goto', то просто используйте его - это на языке по какой-то причине. Это тоже не зло, - это обычно появляется в злых узорах и поэтому должно служить сигналом, чтобы остановить и попытаться определить такие шаблоны (а не окунуться в «OMG' goto', это все неправильно »паника). –

+0

Goto не является неотъемлемым злом, но это лекарство от шлюза, плохое, ленивое. Из всех способов управления потоком обычно бывает худшее. –

10

обмените попытку/задвижку структуру с внутренним во время цикла:

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

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

while 
{ 
    // outer loop 
     try 
     { 
      myMethodWithWhileLoopThatThrowsException() 
     } 
     catch 
     { 
      // how do I continue on the outer loop from here? 
      continue; 
     } 
    } 
} 
+0

Это проблематично, потому что отдельный метод не будет иметь доступ к существующим локальным переменным. – zvrba

+4

Именно поэтому Microsoft предоставила нам функциональные параметры. – Welbog

+0

передайте переменные в качестве параметров или если необходимы побочные эффекты, отправьте их как анонимный делегат, который будет выполнен в методе. Тогда компилятор создаст закрытие, сохранив локальную область. –

18

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

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

Продолжение используется для перехода в верхнюю часть текущего контура.

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

+2

Проблема с этим методом заключается в том, что есть дополнительная работа, которая должна выполняться между концом внутреннего цикла и концом внешнего цикла, это будет выполняться при вызове 'break', но не будет выполняться при вызове' continue'. Вам понадобится флаг, если вам нужно, чтобы этот код не выполнялся. Я не говорю, что этот ответ неправильный (черт возьми, я его поддержал), я говорю, что это обманчиво просто. – Welbog

2

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

1

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

while 
{ 
    // outer loop 

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

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

while 
{ 
    // outer loop 

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

Я считаю, что это то, что вы искали, чтобы исправить? Спасибо!

-2

Используйте собственный тип исключения, например 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) 
    { ; } 
} 

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

+4

Вы причиняете мне боль, используя исключения для нет никакой другой цели, кроме управления потоком. Нет нисходящего потока, просто обиженные чувства.:( –

+3

Это заставляет меня хотеть рвать всюду –

+2

Это немного более решительно, чем «обидеть мои чувства». –

22
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 ; 
    } 

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

+1

Я вижу, что вы там делали – Mastenka

+2

Невозможно скомпилировать, если вы явно не добавите пустую точка с запятой оператора ('REPEAT:;') – matpop

0
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; 
      } 
     } 
    } 

} 

} 

 Смежные вопросы

  • Нет связанных вопросов^_^