Выход из вложенной петли


216

Если у меня есть цикл for, который вложен в другой, как я могу эффективно выйти из обоих циклов (внутреннего и внешнего) как можно быстрее?

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

Какой быстрый и приятный способ сделать это?

Я думал, что исключения не дешевы / должны создаваться только в действительно исключительном состоянии и т. Д. Поэтому я не думаю, что это решение было бы хорошим с точки зрения производительности.

Я не чувствую, что правильно использовать новые возможности .NET (анон методы), чтобы сделать что-то довольно фундаментальное.


Я просто хотел убедиться: зачем ты это делаешь?
Джон Лимджап

3
Почему вы не хотите использовать логическое значение? Что не так с этим?
Энтони

В VB.net вы можете обернуть оператор try / finally (без перехвата) вокруг произвольного числа циклов, а затем «exit try» завершит их все в любой точке.
Brain2000

Ответы:


206

Ну, gotoно это некрасиво и не всегда возможно. Вы также можете поместить циклы в метод (или anon-метод) и использовать returnдля возврата к основному коду.

    // goto
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            goto Foo; // yeuck!
        }
    }
Foo:
    Console.WriteLine("Hi");

против:

// anon-method
Action work = delegate
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits anon-method
        }
    }
};
work(); // execute anon-method
Console.WriteLine("Hi");

Обратите внимание, что в C # 7 мы должны получить «локальные функции», что (синтаксис tbd и т. Д.) Означает, что оно должно работать примерно так:

// local function (declared **inside** another method)
void Work()
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits local function
        }
    }
};
Work(); // execute local function
Console.WriteLine("Hi");

49
В такой ситуации я не думаю, что использование goto хуже, чем обычное использование чего-то вроде break (в конце концов, они оба просто безусловные переходы к метке, просто с break неявная метка).
Грег Бич

37
иногда goto менее злой, чем альтернативы
seanb

7
@BeowulfOF - break будет выходить только из внутреннего цикла, а не из внутреннего и внешнего цикла.
Грег Бич

36
Сам Гото не безобразен. То, что является уродливым, является злоупотреблением goto, который приводит к коду спагетти. Использование goto для выхода из вложенного цикла совершенно нормально. Кроме того, обратите внимание, что все операции разрыва, продолжения и возврата, с точки зрения структурного программирования, едва ли лучше, чем goto - в основном это одно и то же, только в более приятной упаковке. Вот почему в чисто структурных языках (таких как оригинальный Паскаль) отсутствуют все три.
el.pescado

11
@ el.pescado совершенно прав: многие программисты вводят в заблуждение, полагая, что они gotoвредны сами по себе , хотя это просто правильный инструмент для выполнения некоторых действий, и, как и многие другие инструменты, могут быть использованы неправильно. Эта религиозная неприязнь gotoоткровенно довольно глупа и определенно ненаучна.
о0 '.

96

C # адаптация подхода, часто используемого в C - установить значение переменной внешнего цикла вне условий цикла (т.е. для цикла, использующего переменную int, INT_MAX -1часто является хорошим выбором):

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (exit_condition)
        {
            // cause the outer loop to break:
            // use i = INT_MAX - 1; otherwise i++ == INT_MIN < 100 and loop will continue 
            i = int.MaxValue - 1;
            Console.WriteLine("Hi");
            // break the inner loop
            break;
        }
    }
    // if you have code in outer loop it will execute after break from inner loop    
}

Как говорится в примечании к коду break, магически не будет переходить к следующей итерации внешнего цикла - поэтому, если у вас есть код вне внутреннего цикла, этот подход требует больше проверок. Рассмотрим другие решения в этом случае.

Этот подход работает с forи whileциклов, но не работает для foreach. В случае, если у foreachвас не будет доступа к коду для скрытого перечислителя, вы не сможете его изменить (и даже если у вас IEnumeratorне будет никакого метода MoveToEnd).

Благодарность авторам комментариев:
i = INT_MAX - 1предложение от Meta
for / foreachкомментарий ygoe .
Правильно IntMaxпо jmbpiano
замечания о коде после внутреннего цикла по blizpasta


@DrG: не будет работать в c # как «оператор break завершает ближайший замкнутый цикл или оператор switch, в котором он появляется». (MSDN)
блиспаста

1
@blizpasta Так? Если он делает условие на внешнем цикле ложным (как он сделал), он выйдет из обоих.
Патрик

51
вы должны использовать i = INT_MAX - 1; в противном случае i ++ == INT_MIN <100 и цикл продолжится
Meta

6
Любая идея на foreach?
ктутник

4
@ktutnik Это не будет работать с foreach, потому что у вас не будет доступа к коду для скрытого перечислителя. Также IEnumerator не имеет некоторого метода MoveToEnd.
ygoe

39

Это решение не относится к C #

Для людей, которые нашли этот вопрос на других языках, Javascript, Java и D позволяют помечать разрывы и продолжать :

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}

40
это грустно, это не может быть сделано с помощью c #, это будет во многих случаях производить более чистый код.
Рикард

17
Я на самом деле был взволнован на секунду, пока я не понял, что это не для C #. :(
Арво Боуэн

1
Эта концепция также предназначена для PowerShell на случай, если кто-то столкнется с этой проблемой. (Они просто помещают двоеточие перед именем метки.) Теперь я знаю, что это не относится к PowerShell ... Этот синтаксис был бы несовместим с метками goto, доступными в C #. PHP использует что-то еще: перерыв 3; Поставьте количество уровней после оператора break.
ygoe

1
Это Java, а не C #
Лука Циглер

Для тех, кто хотел бы видеть эту функцию в C #, разместите свои пожелания здесь, пожалуйста, 😉 github.com/dotnet/csharplang/issues/869#issuecomment-326606003
m93a

28

Используйте подходящую защиту во внешней петле. Установите предохранитель во внутренней петле, прежде чем сломать.

bool exitedInner = false;

for (int i = 0; i < N && !exitedInner; ++i) {

    .... some outer loop stuff

    for (int j = 0; j < M; ++j) {

        if (sometest) {
            exitedInner = true;
            break;
        }
    }
    if (!exitedInner) {
       ... more outer loop stuff
    }
}

Или еще лучше, абстрагируйте внутренний цикл в метод и выйдите из внешнего цикла, когда он вернет false.

for (int i = 0; i < N; ++i) {

    .... some outer loop stuff

    if (!doInner(i, N, M)) {
       break;
    }

    ... more outer loop stuff
}

4
За исключением того, что ОП сказал: «Я не хочу использовать логическое значение».
LeopardSkinPillBoxHat

Очень Паскаль ... Я бы предпочел использовать goto, хотя обычно я избегаю их как чума.
Джонатан Леффлер

21

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

ПЕРЕЙТИ К:

for ( int i = 0; i < 10; ++i ) {
   for ( int j = 0; j < 10; ++j ) {
      // code
      if ( break_condition ) goto End;
      // more code
   }
}
End: ;

Состояние:

bool exit = false;
for ( int i = 0; i < 10 && !exit; ++i ) {
   for ( int j = 0; j < 10 && !exit; ++j ) {
      // code
      if ( break_condition ) {
         exit = true;
         break; // or continue
      }
      // more code
   }
}

Исключение:

try {
    for ( int i = 0; i < 10 && !exit; ++i ) {
       for ( int j = 0; j < 10 && !exit; ++j ) {
          // code
          if ( break_condition ) {
             throw new Exception()
          }
          // more code
       }
    }
catch ( Exception e ) {}

2
это все хакерские обходные пути, где было бы очень чисто просто включить метод и использовать ранний возврат
Дастин Гетц

3
:) верно, это простое решение, но вам придется передавать все необходимые локальные данные в метод в качестве аргументов ... Это одно из немногих мест, где goto может быть подходящим решением
Дэвид Родригес - dribeas

Метод Condition даже не работает, потому что «больше кода» будет выполнено один раз после выхода из внутреннего цикла перед выходом из внешнего цикла. Метод GOTO работает, но делает именно то, что сказал автор, которого они не хотят делать. Метод Exception работает, но он уродливее и медленнее, чем GOTO.
Программист Windows

Я бы выделил эту точку с запятой после метки. Таким образом, эта метка может быть даже в конце блока. +1
Тамас Гегедус

@Windowsprogrammer ОП спросил: «Какой быстрый и приятный способ сделать это?» Goto является предпочтительным решением: чистый и лаконичный, без использования отдельного метода.
Suncat2000

16

Можно ли реорганизовать вложенный цикл for в закрытый метод? Таким образом, вы можете просто «вернуться» из метода, чтобы выйти из цикла.


С дополнительным преимуществом сделать ваш оригинальный метод короче :-)
Martin Capodici

В некоторых случаях лямбды C ++ 11 делают это легко:[&] { ... return; ... }();
BCS

14

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

Я считаю, что «эмоции», которые люди испытывают, в gotoконечном итоге сводятся к пониманию кода и (заблуждениям) о возможных последствиях для производительности. Прежде чем ответить на вопрос, я сначала расскажу о некоторых деталях его компиляции.

Как мы все знаем, C # компилируется в IL, который затем компилируется в ассемблер с использованием компилятора SSA. Я немного расскажу о том, как все это работает, а затем попытаюсь ответить на сам вопрос.

От C # до IL

Сначала нам нужен кусок кода C #. Давайте начнем с простого:

foreach (var item in array)
{
    // ... 
    break;
    // ...
}

Я сделаю это шаг за шагом, чтобы дать вам хорошее представление о том, что происходит под капотом.

Первый перевод: из foreachэквивалентного forцикла (Примечание: здесь я использую массив, потому что я не хочу вдаваться в подробности IDisposable - в этом случае мне также придется использовать IEnumerable):

for (int i=0; i<array.Length; ++i)
{
    var item = array[i];
    // ...
    break;
    // ...
}

Второй перевод: forи breakпереводится в более простой эквивалент:

int i=0;
while (i < array.Length)
{
    var item = array[i];
    // ...
    break;
    // ...
    ++i;
}

И третий перевод (это эквивалент IL-кода): мы меняемся breakи whileпревращаемся в ветку:

    int i=0; // for initialization

startLoop:
    if (i >= array.Length) // for condition
    {
        goto exitLoop;
    }
    var item = array[i];
    // ...
    goto exitLoop; // break
    // ...
    ++i;           // for post-expression
    goto startLoop; 

Хотя компилятор делает все это за один шаг, он дает вам представление о процессе. Код IL, который развивается из программы C #, является буквальным переводом последнего кода C #. Вы можете увидеть здесь: https://dotnetfiddle.net/QaiLRz (нажмите «посмотреть IL»)

Одна вещь, которую вы здесь заметили, заключается в том, что во время процесса код становится более сложным. Самый простой способ убедиться в этом - это то, что нам требовалось все больше и больше кода для подтверждения того же самого. Можно также утверждать , что foreach, for, whileи breakна самом деле являются короткими руками для goto, что отчасти верно.

От IL до Ассемблера

JIT-компилятор .NET является SSA-компилятором. Я не буду вдаваться во все детали формы SSA и о том, как создать оптимизирующий компилятор, это слишком много, но может дать общее представление о том, что произойдет. Для более глубокого понимания лучше начать с чтения по оптимизации компиляторов (мне нравится эта книга для краткого введения: http://ssabook.gforge.inria.fr/latest/book.pdf ) и LLVM (llvm.org) ,

Каждый оптимизирующий компилятор опирается на тот факт, что код прост и следует предсказуемым шаблонам . В случае циклов FOR мы используем теорию графов для анализа ветвей, а затем оптимизируем такие вещи, как cycli в наших ветвях (например, ветвления в обратном направлении).

Однако теперь у нас есть прямые ветви для реализации наших циклов. Как вы могли догадаться, это на самом деле один из первых шагов, которые JIT собирается исправить, например:

    int i=0; // for initialization

    if (i >= array.Length) // for condition
    {
        goto endOfLoop;
    }

startLoop:
    var item = array[i];
    // ...
    goto endOfLoop; // break
    // ...
    ++i;           // for post-expression

    if (i >= array.Length) // for condition
    {
        goto startLoop;
    }

endOfLoop:
    // ...

Как видите, у нас теперь есть обратная ветвь, которая является нашей маленькой петлей. Единственная вещь, которая все еще противна здесь, - это ветвь, в которой мы оказались из-за нашего breakзаявления. В некоторых случаях мы можем перемещать это таким же образом, но в других это остается.

Так почему же это делает компилятор? Что ж, если мы сможем развернуть цикл, мы сможем его векторизовать. Мы могли бы даже доказать, что добавляются только константы, что означает, что весь наш цикл может исчезнуть в воздухе. Подводя итог: сделав шаблоны предсказуемыми (делая предсказуемые ветви), мы можем доказать, что в нашем цикле выполняются определенные условия, что означает, что мы можем творить чудеса во время оптимизации JIT.

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

В этот момент вы также должны понимать, что простое foreachпредсказуемее, чем набор gotoутверждений, которые встречаются повсюду. С точки зрения (1) читабельности и (2) с точки зрения оптимизатора, это и лучшее решение.

Еще одна вещь, о которой стоит упомянуть, это то, что для оптимизации компиляторов очень важно назначать регистры переменным (процесс, называемый распределением регистров ). Как вы, возможно, знаете, в вашем ЦП имеется только конечное число регистров, и они являются самыми быстрыми частями памяти в вашем оборудовании. Переменные, используемые в коде, который находится в самом внутреннем цикле, с большей вероятностью получат назначенный регистр, в то время как переменные вне вашего цикла менее важны (потому что этот код, вероятно, ударил меньше).

Помогите, слишком много сложностей ... что мне делать?

Суть в том, что вы всегда должны использовать языковые конструкции, которые есть в вашем распоряжении, что обычно (неявно) создает предсказуемые шаблоны для вашего компилятора. Старайтесь избегать странных ветвей , если это возможно ( в частности: break, continue, gotoили returnв середине ничего).

Хорошая новость заключается в том, что эти предсказуемые шаблоны легко читаются (для людей) и легко обнаруживаются (для компиляторов).

Один из этих шаблонов называется SESE, что означает однократный выход.

И теперь мы подошли к реальному вопросу.

Представьте, что у вас есть что-то вроде этого:

// a is a variable.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...

     if (i*j > a) 
     {
        // break everything
     }
  }
}

Самый простой способ сделать это предсказуемой моделью - это просто ifполностью исключить :

int i, j;
for (i=0; i<100 && i*j <= a; ++i) 
{
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
}

В других случаях вы также можете разделить метод на 2 метода:

// Outer loop in method 1:

for (i=0; i<100 && processInner(i); ++i) 
{
}

private bool processInner(int i)
{
  int j;
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
  return i*j<=a;
}

Временные переменные? Хорошо, плохо или безобразно?

Вы можете даже решить вернуть логическое значение из цикла (но я лично предпочитаю форму SESE, потому что именно так ее увидит компилятор, и я думаю, что ее чище читать).

Некоторые люди думают, что лучше использовать временную переменную, и предлагают решение, подобное этому:

bool more = true;
for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j) 
  {
     // ...
     if (i*j > a) { more = false; break; } // yuck.
     // ...
  }
  if (!more) { break; } // yuck.
  // ...
}
// ...

Я лично против такого подхода. Посмотрите еще раз, как код компилируется. Теперь подумайте, что это будет делать с этими хорошими, предсказуемыми шаблонами. Получить картину?

Хорошо, позвольте мне изложить это. Что произойдет, так это:

  • Компилятор запишет все как ветки.
  • В качестве шага оптимизации компилятор выполнит анализ потока данных, пытаясь удалить странную moreпеременную, которая используется только в потоке управления.
  • В случае успеха переменная moreбудет удалена из программы, и останутся только ветви. Эти ветви будут оптимизированы, поэтому вы получите только одну ветку из внутреннего цикла.
  • В случае неудачи переменная moreопределенно используется в самом внутреннем цикле, поэтому, если компилятор не оптимизирует ее, у нее есть высокая вероятность быть назначенной регистру (который пожирает ценную память регистра).

Итак, подведем итоги: оптимизатор в вашем компиляторе постигнет кучу хлопот, чтобы выяснить, что moreиспользуется только для потока управления, и в лучшем случае преобразует его в одну ветку за пределами внешнего для петля.

Другими словами, в лучшем случае это будет эквивалентно следующему:

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...
     if (i*j > a) { goto exitLoop; } // perhaps add a comment
     // ...
  }
  // ...
}
exitLoop:

// ...

Мое личное мнение об этом довольно простое: если это то, что мы намеревались все время, давайте сделаем мир проще как для компилятора, так и для удобства чтения, и напишем это сразу.

ТЛ; др:

Нижняя граница:

  • Если возможно, используйте простое условие в цикле for. Придерживайтесь языковых конструкций высокого уровня, которые есть в вашем распоряжении.
  • Если ничего не получается и у вас остается либо, gotoлибо bool more, предпочитайте первое.

OTOH: У меня очень редко возникает желание прерывать внешние циклы или желать goto-эквивалент (и много кода, который действительно может быть написан более четко), хотя это может быть более распространенным в других областях ... сегодня был такой случай, но это было в значительной степени из-за yield return. То есть, хотя легко показать, что есть некоторые полезные случаи, для большинства кода «уровня приложения» это, вероятно, очень низкий уровень разочарования в C #, исключая те, которые «только что пришли» из C / C ++ ;-) Я также иногда пропускаю Руби "throw" (не исключая раскручивание) иногда, поскольку он также вписывается в эту область.
user2864740

1
@ Suncat2000 Простое введение большего количества кода, такого как переменные и условные ветви, чтобы избежать использования a goto, не сделает ваш код более читабельным - я бы сказал, что все происходит с точностью до наоборот: на самом деле ваш поток управления будет содержать больше узлов - что в значительной степени определение сложности. Просто избегать этого, чтобы помочь самодисциплине, звучит контрпродуктивно с точки зрения читабельности.
атлас

@atlaste: Извините, позвольте мне изложить свой комментарий более четко. То, что я должен был сказать, было: хорошее объяснение. Но люди, избегающие goto, не о производительности; Речь идет о том, чтобы избежать злоупотреблений, которые вызывают отсутствие читабельности. Мы можем предположить, что люди, которые избегают, gotoпросто не имеют дисциплины, чтобы не злоупотреблять ею.
Suncat2000

@atlaste, на каком языке это "ackomplish"?
Пейсер

11

включите функцию / метод и используйте ранний возврат или переставьте ваши циклы в предложение while. Перейти / исключения / все, что, конечно, здесь не подходит.

def do_until_equal():
  foreach a:
    foreach b:
      if a==b: return

10

Вы просили сочетание быстрого, красивого, без использования логического выражения, без использования goto и C #. Вы исключили все возможные способы делать то, что вы хотите.

Самый быстрый и наименее уродливый способ - использовать goto.


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

@Windowsprogramm: OP не просил "не использовать goto". Он не хотел «идти к другому методу». Вопрос был далек от того, чтобы исключить все возможные пути, но вы правы в том, что goto здесь лучше.
Suncat2000

5

Иногда приятно абстрагировать код в его собственную функцию, а затем использовать ранний возврат - хотя ранние возвраты являются злом:)

public void GetIndexOf(Transform transform, out int outX, out int outY)
{
    outX = -1;
    outY = -1;

    for (int x = 0; x < Columns.Length; x++)
    {
        var column = Columns[x];

        for (int y = 0; y < column.Transforms.Length; y++)
        {
            if(column.Transforms[y] == transform)
            {
                outX = x;
                outY = y;

                return;
            }
        }
    }
}

1
но тогда это учитывает традиционное решение, заданное ОП
Сурья Пратап

2

Я видел много примеров, которые используют «break», но ни один из которых не использует «continue».

Это все еще потребовало бы флага некоторого вида во внутреннем цикле:

while( some_condition )
{
    // outer loop stuff
    ...

    bool get_out = false;
    for(...)
    {
        // inner loop stuff
        ...

        get_out = true;
        break;
    }

    if( get_out )
    {
        some_condition=false;
        continue;
    }

    // more out loop stuff
    ...

}

1

С тех пор как я впервые увидел breakC на пару десятилетий назад, эта проблема меня раздражала. Я надеялся, что некоторое улучшение языка будет иметь расширение, которое будет работать таким образом:

break; // our trusty friend, breaks out of current looping construct.
break 2; // breaks out of the current and it's parent looping construct.
break 3; // breaks out of 3 looping constructs.
break all; // totally decimates any looping constructs in force.

3
Затем программист по обслуживанию вставит другой уровень вложенности, исправит некоторые операторы break и сломает некоторые другие операторы break. Исправление для этого состоит в том, чтобы взломать метку вместо этого. Это действительно было предложено, но прагматики используют вместо этого ярлык.
Программист Windows

Подождите, кто больше занимается программированием обслуживания? :)
Джесси С. Slicer

2
В JavaScript даже есть помеченные блоки / операторы break. devguru.com/Technologies/ecmascript/quickref/break.html
Дэвид Грант

@ Крис Бартоу: круто! сделал мое Рождество :) @ Дэвид Грант: так что, похоже, JS перерыв == C's Goto?
Джесси С. Слайсер

D пометил перерыв / продолжается
BCS

0

Из моих студенческих дней я помню, что было сказано, что математически доказуемо, что вы можете делать что-либо в коде без goto (то есть нет ситуации, когда goto является единственным ответом). Таким образом, я никогда не использую goto's (просто мое личное предпочтение, не предполагая, что я прав или нет)

В любом случае, чтобы вырваться из вложенных циклов, я делаю что-то вроде этого:

var isDone = false;
for (var x in collectionX) {
    for (var y in collectionY) {
        for (var z in collectionZ) {
            if (conditionMet) {
                // some code
                isDone = true;
            }
            if (isDone)
                break;
        }
        if (isDone) 
            break;
    }
    if (isDone)
        break;
}

... я надеюсь, что те, кто любит меня, помогают анти-гото "фанатам" :)


Извините, что говорю вам, но ваш лектор виноват в вашем состоянии. Если он потрудился заставить вас изучать ассемблер, то вы знаете, что «goto» - это просто прыжок (конечно, я игнорирую тот факт, что это ac # вопрос).
cmroanirgo

0

Вот как я это сделал. Все еще обходной путь.

foreach (var substring in substrings) {
  //To be used to break from 1st loop.
  int breaker=1;
  foreach (char c in substring) {
    if (char.IsLetter(c)) {
      Console.WriteLine(line.IndexOf(c));
      \\setting condition to break from 1st loop.
      breaker=9;
      break;
    }
  }
  if (breaker==9) {
    break;
  }
}


0

Самый простой способ завершить двойной цикл - это напрямую завершить первый цикл.

string TestStr = "The frog jumped over the hill";
char[] KillChar = {'w', 'l'};

for(int i = 0; i < TestStr.Length; i++)
{
    for(int E = 0; E < KillChar.Length; E++)
    {
        if(KillChar[E] == TestStr[i])
        {
            i = TestStr.Length; //Ends First Loop
            break; //Ends Second Loop
        }
    }
}

Это не работает, так как с помощью break вы закончите внутренний цикл. Внешний цикл будет продолжать повторяться.
Алекс Лев

0

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

    static void Main(string[] args)
    {
        bool isBreak = false;
        for (int i = 0; ConditionLoop(isBreak, i, 500); i++)
        {
            Console.WriteLine($"External loop iteration {i}");
            for (int j = 0; ConditionLoop(isBreak, j, 500); j++)
            {
                Console.WriteLine($"Inner loop iteration {j}");

                // This code is only to produce the break.
                if (j > 3)
                {
                    isBreak = true;
                }                  
            }

            Console.WriteLine("The code after the inner loop will be executed when breaks");
        }

        Console.ReadKey();
    }

    private static bool ConditionLoop(bool isBreak, int i, int maxIterations) => i < maxIterations && !isBreak;   

С помощью этого кода мы получаем следующий вывод:

  • Итерация внешнего цикла 0
  • Внутренний цикл итерации 0
  • Внутренний цикл итерации 1
  • Внутренний цикл итерации 2
  • Внутренний цикл итерации 3
  • Внутренний цикл итерации 4
  • Код после внутреннего цикла будет выполняться при перерывах

0

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

// self-invoked-anonymous-function
new Action(() =>
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits self invoked lambda expression
        }
    }
})();
Console.WriteLine("Hi");

-3

Создайте пользовательское исключение, которое выходит из внешнего цикла.

Он работает for, foreachили whileили любой вид цикла и любой язык , который использует try catch exceptionблок

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}

-4
         bool breakInnerLoop=false
        for(int i=0;i<=10;i++)
        {
          for(int J=0;i<=10;i++)
          {
              if(i<=j)
                {
                    breakInnerLoop=true;
                    break;
                }
          }
            if(breakInnerLoop)
            {
            continue
            }
        }

Какая существенная разница с ответом dviljoen?
Герт Арнольд

Это не сработает, потому что вы не проверяете условие «breakInnerLoop» во внешнем цикле for, поэтому вы просто переходите к следующему циклу, также вы записали j и J и пропустили точку с запятой, которая не будет компилироваться.
Себастьян

-4

Как я вижу, вы приняли ответ, в котором человек ссылается на ваше заявление goto, где в современном программировании и, по мнению экспертов, goto является убийцей, мы назвали его убийцей в программировании, у которого есть некоторые определенные причины, которые я не буду обсуждать здесь на данный момент, но решение вашего вопроса очень простое, вы можете использовать логический флаг в сценарии такого рода, как я продемонстрирую в своем примере:

            for (; j < 10; j++)
            {
                //solution
                bool breakme = false;
                for (int k = 1; k < 10; k++)
                {
                   //place the condition where you want to stop it
                    if ()
                    {
                        breakme = true;
                        break;
                    }
                }

                if(breakme)
                    break;
               }

просто и понятно. :)


прочитайте вопрос, прежде чем предлагать перерыв. Отсюда и недостатки.
cmroanirgo

1
прочитайте его сейчас, но когда я разместил этот ответ, отредактированная версия, не использующая логическое значение, не была добавлена, поэтому я опубликовал этот ответ ... но спасибо за downvote!
Стив

1
Это не личная вещь. К сожалению, голосование теперь заблокировано;) Это необходимая часть SO, чтобы получить лучшие ответы на вершину (что не всегда происходит)
cmroanirgo

-6

Вы даже смотрели на breakключевое слово? Оо

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

<?php
for(...) {
    while(...) {
        foreach(...) {
            break 3;
        }
    }
}

Если вы думаете о breakтом, чтобы быть функцией, какbreak() , то ее параметром будет число циклов, из которых нужно выйти. Поскольку мы находимся в третьем цикле кода, мы можем выйти из всех трех.

Руководство: http://php.net/break


13
Не вопрос php.
Зерга

-10

Я думаю, если вы не хотите делать «булеву вещь», единственное решение - бросить. Что вы, очевидно, не должны делать ..!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.