Вывод из петли foreach изнутри коммутационного блока - программирование
Подтвердить что ты не робот

Вывод из петли 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;
    }
}
4b9b3361

Ответ 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().