Вывод из петли foreach изнутри коммутационного блока
Как вы выходите из цикла foreach в пределах блока переключения?
Обычно вы используете break, но если вы используете разрыв в блоке коммутатора, он просто вытащит вас из блока switch и цикл 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;
}
}
Ответы
Ответ 1
Ваше решение в большинстве случаев является наиболее распространенным вариантом. При этом я поставил вашу проверку на выход в конце:
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;
}
Другой основной вариант - реорганизовать ваш код и вывести оператор switch и foreach loop в отдельный метод. Тогда вы можете просто return
изнутри оператора switch.
Ответ 2
Логическое значение имеет один путь. Другой использует метки и 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: некоторые люди упомянули о том, чтобы вывести цикл в отдельный метод, чтобы вы могли использовать return. Я вижу преимущество этого, поскольку он не требует goto, а также упрощает исходную функцию, содержащую цикл. Однако, если цикл прост и является основной целью функции, которая его содержит, или если цикл использует переменные out или ref, то, вероятно, лучше всего оставить его на месте и использовать goto. Фактически, поскольку goto и ярлык выделяются, это, вероятно, делает код более четким, а не clunkier. Включение его в отдельную функцию может упростить чтение простого кода.
Ответ 3
Вы можете извлечь цикл foreach в отдельный метод и использовать оператор 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.
}
}
Ответ 4
Честно? Это, пожалуй, единственная ситуация, когда полностью и правильно использовать 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
Ответ 5
Он не сильно отличается от вашего флага 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;
}
Ответ 6
Всегда существует возможность перестроить ваш код, чтобы вы могли return
из оператора switch
.
Ответ 7
Основываясь на документации MSDN в инструкции break
, она позволяет остановить только верхнюю область.
В этом случае вы можете использовать оператор goto
, чтобы оставить цикл foreach
.
Если вы не хотите использовать оператор goto
, ваше решение кажется лучшим.
В качестве дополнительной заметки вы можете улучшить свой код, протестировав флаг exitLoop
в конце итерации, сохранив стоимость одного вызова перечислителя.
Ответ 8
Ламе, я знаю, но это все, что вы можете с этим поделать.
Вы всегда можете преобразовать его в цикл while и добавить 'exitLoop' в качестве условия, которое должно быть выполнено. Внутри коммутатора вы можете вызвать continue, чтобы пропустить оставшуюся часть текущего прохода, и поскольку вы установили бы exitLoop на false, он выйдет так же, как и break. Даже если это не совсем то, о чем вы спрашиваете, возможно, это более элегантно?
Ответ 9
Некоторые языки (я знаю PHP - это один, не уверенный в других) позволяет указать, сколько структур управления вам нужно вырваться из
break n;
где 1 подразумевается, если вы просто перерыв
break 2 будет делать то, что вы описали, было ли доступно на С#. Я не считаю, что случай, так что ваш флаг выхода, вероятно, является лучшим решением.
Ответ 10
Вы можете сделать это с помощью Try/Catch.
Но это может быть не лучшая идея в мире, потому что это вызывает проблемы с производительностью и показывает неприятные строки в окне отладки.
try
{
foreach (var v in myCollection)
{
switch (v.id)
{
case 1:
if (true)
{
throw new SystemException("Break");
}
break;
case 2;
break;
}
}
} catch {}
Ответ 11
Преобразуйте оператор switch() в ряд операторов "if() else if() [...] else"
, чтобы break
вышел из цикла foreach()
.