2010-10-28 3 views
46

Как вы вырваться из цикла foreach в пределах блока переключения?Вырыв из петли foreach изнутри коммутационного блока

Обычно вы используете перерыв, но если вы используете перерыв в распределительном блоке он будет просто получить вас из блока переключателей и цикл Еогеаспа продолжит выполнение:

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

То, что я сейчас делаю когда мне нужно вырваться из foreach, в то время как в блоке switch устанавливается значение bool, помещенное за пределами цикла, в true и проверяющее значение этого bool каждый раз, когда вводится foreach и перед входом в блок переключателя. Что-то вроде этого:

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

Это работает, но я продолжаю думать, что должен быть лучшим способом сделать это я не в курсе ...

EDIT: Интересно, почему это не было реализовано в .NET действительно аккуратный способ работает в PHP как упоминалось @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; 
    } 
} 
+0

Не уверен, что существует лучший способ, но он будет похож на необходимость вырваться из внешнего цикла, находясь внутри внутреннего цикла. До сих пор я так и не нашел этого, и, следовательно, использование значения флага кажется единственным способом. –

+0

Возможный дубликат [Break out of while loop, содержащий оператор switch] (http://stackoverflow.com/questions/1987379/break-out-of-a-while-loop-that-contains-a-switch- заявление) – ruffin

ответ

46

Ваше решение в большинстве случаев является наиболее распространенным вариантом в этом случае. Это, как говорится, я бы поставил чек на выход в конце:

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

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

9

Честно? Это, пожалуй, единственная ситуация, когда она полностью справедлива и собственно использовать 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 
+2

Зачем использовать goto? Намного лучше извлечь метод. Каждый раз, когда вы используете инструкцию goto, умирает котенок. –

+3

Зачем извлекать метод, когда вы можете просто перейти? На самом деле это шесть в одном, полдюжины других. Если цикл достаточно мал для того, чтобы в первую очередь принадлежать одной функции, кажется немного странным реорганизовать его исключительно для того, чтобы избежать чистой и понятной goto. – siride

+0

Потому что использование goto вызывает боль. Ваши методы должны быть как можно короче. И если это так, вам никогда не понадобится утверждение goto. –

15

Вы можете извлечь свой Еогеасп цикл в отдельный метод и использовать return заявление. Или вы могли бы сделать так:

 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. 
      } 
     } 
+0

+1, это намного чище –

1

Lame, я знаю, но это все, что вы можете с этим поделать.

Вы всегда можете преобразовать его в цикл while и добавить 'exitLoop' в качестве условия, которое должно быть выполнено. Внутри коммутатора вы можете вызвать continue, чтобы пропустить оставшуюся часть текущего прохода, и поскольку вы установили бы exitLoop на false, он выйдет так же, как и break. Хотя это не совсем то, о чем вы спрашиваете, возможно, это более элегантно?

4

Всегда есть возможность перестроить ваш код, чтобы вы могли return из инструкции switch.

2

Основано на MSDN documentation в инструкции break, оно позволяет остановить самый верхний объем.

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

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

+0

Который я нахожу довольно неудачным, тем более, что Java позволяет вам вырваться из многоуровневых конструкций контура/переключателя с синтаксисом «break 2». – siride

20

Булевский - это один из способов. Другой использует метки и goto. Я знаю, что люди считают, что это кардинальный грех, но разумно (ОЧЕНЬ разумно), он может быть полезен. В этом случае поместите метку только за конец цикла foreach. Когда вы хотите выйти из цикла, просто перейдите к этой метке. Например:

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: некоторые люди упоминали принимая цикл в отдельный метод, так что вы можете использовать возврат. Я вижу преимущество этого, поскольку он не требует goto, а также упрощает исходную функцию, содержащую цикл. Однако, если цикл прост и является основной целью функции, которая его содержит, или если цикл использует переменные out или ref, то, вероятно, лучше всего оставить его на месте и использовать goto. Фактически, поскольку goto и ярлык выделяются, это, вероятно, делает код более четким, а не clunkier. Включение его в отдельную функцию может упростить чтение простого кода.

+0

Точно. Хорошо сказано. – TomTom

+6

Не используйте goto. Это делает код более сложным. Метод извлечения намного лучше. –

+6

Лучше, если бы вы указали * почему * goto делает код более сложным, особенно когда альтернатива столь же сложна и перемещает код вдали от исходного контекста. – siride

6

Это на самом деле не отличается от вашего exitLoop флага, но это может быть более удобным для чтения, если вы извлекаете метод ...

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

Я думаю, что это отвечает на вопрос лучше всего. –

1

Некоторые языки (я знаю PHP это один, не уверен, о других) позволяют вам указать, сколько структур управления вы хотите вырваться с

break n;
где 1 подразумевается, если вы просто перерыв

break 2 будет делать то, что вы описали, был ли он доступен на C#. Я не верю, что это так, поэтому ваш флаг выхода, вероятно, является лучшим решением.

+0

Мне очень нравится, как это было решено в PHP ... –

+0

Да, им не много для использования перерыва, но это довольно круто –

+0

У PARI/GP также есть функция 'break (n)'. –

-2

Преобразуйте оператор switch() в строку операторов "if() else if() [...] else" так, чтобы break выходил из цикла foreach().

+0

Всегда есть несколько вариантов замены одного метода другим. Но это не отвечает на вопрос. Вопрос: как вы выходите из цикла foreach в пределах блока переключения? –

0

Вы можете сделать это с помощью Try/Catch. Но это может быть не лучшая идея в мире, потому что это вызывает проблемы с производительностью и показывает неприятные строки в окне отладки.

try 
{ 
foreach (var v in myCollection) 
    { 
     switch (v.id) 
     { 
      case 1: 
       if (true) 
       { 
        throw new SystemException("Break"); 
       } 
       break; 
      case 2; 
       break; 
     } 
    } 
} catch {} 
+0

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

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