Как вырваться из 2 циклов без переменной флага в С#?

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

foreach(DataGridViewRow row in grid.Rows)
{
    foreach(DataGridViewCell cell in row.Cells)
    {
        if(cell.Value == myValue)
        {
            //Do Something useful
            //break out of both foreach loops.
        }
    }
}

Как это делается на С#. В Java я мог бы использовать метку, чтобы назвать самый внешний цикл, а затем разбить этот цикл, но я не могу найти эквивалент в С#.

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

Спасибо,

Ответы

Ответ 1

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

public void DoubleLoop()
{
    for(int i = 0; i < width; i++)
    {
        for(int j = 0; j < height; j++)
        {
            if(whatever[i][j]) break; // let make this a "double" break
        }
    }
}

переходит в

public bool CheckWhatever(int whateverIndex)
{
    for(int j = 0; j < height; j++)
    {
        if(whatever[whateverIndex][j]) return false;
    }

    return true;
}

public void DoubleLoop()
{
    for(int i = 0; i < width; i++)
    {
        if(!CheckWhatever(i)) break;
    }
}

Конечно, не стесняйтесь упрощать это с помощью LINQ или что-то еще (вы можете поставить CheckWhatever в условие цикла тоже). Это просто многословная демонстрация принципа.

Ответ 2

1

foreach(DataGridViewRow row in grid.Rows)
   foreach(DataGridView cell in row.Cells)
      if (cell.Value == somevalue) {
         // do stuff
         goto End;
      }
End:
   // more stuff

2

void Loop(grid) {
    foreach(row in grid.Rows)
       foreach(cell in row.Cells)
           if (something) {
               // do stuff   
               return;
           }
}

3

var cell = (from row in grid.Rows.OfType<DataGridViewRow>()
            from cell in row.Cells.OfType<DataGridViewCell>()
            where cell.Value == somevalue
            select cell
   ).FirstOrDefault();

if (cell != null) {
   // do stuff
}

Ответ 3

Хотя многие из вышеперечисленных решений верны и отвечают на ваш вопрос, я бы сделал шаг назад и спросил себя: "Есть ли другой способ более четко представлять семантику программы?"

Я хотел бы написать код следующим образом:

var query = from row in grid.Rows
            from cell in row.Cells
            where cell.Value == myValue
            select cell;
if (query.Any())
{
  // do something useful;
}

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

Ответ 4

Я бы просто привязал петли к функции и возвратил функцию как способ выхода из цикла для моего решения.

Ответ 5

        foreach (DataGridViewRow row in grid.Rows)
        {
            foreach (DataGridViewCell cell in row.Cells)
            {
                if (cell.Value == myValue)
                {
                    goto EndOfLoop;
                    //Do Something useful
                    //break out of both foreach loops.
                }
            }

        }
        EndOfLoop: ;

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

EDIT: Просто добавьте немного больше предупреждений; обычно считается плохой практикой использования goto, поскольку они быстро могут привести к коду спагетти, который (почти) невозможно поддерживать. Говоря это, он был включен в язык С# и доступен для использования, поэтому есть люди, которые считают, что у него есть действующие обычаи. Знайте, что функция существует и используется с большой осторожностью.

Ответ 6

Для полноты также есть неправильный способ:

try
{
    foreach(DataGridViewRow row in grid.Rows)
        foreach(DataGridViewCell cell in row.Cells)
            if(cell.Value == myValue)
               throw new FoundItemException(cell);
}
catch (FoundItemException ex)
{
    //Do Something useful with ex.item
}

Ответ 7

С# имеет инструкцию goto. Фактически, пример в MSDN использует его, чтобы вырваться из дважды вложенного цикла.

Ответ 8

Лучший способ - не делать этого. Шутки в сторону; если вы хотите найти первое появление чего-то в своих вложенных циклах, а затем закончить поиск, то то, что вы хотите сделать, - это НЕ проверять каждый элемент, который явно является тем, что делает конструкция foreach. Я бы рекомендовал использовать регулярный цикл с флагом завершения в инварианте цикла.

Ответ 9

Вы можете написать класс, который реализует IEnumerator <T> в общем случае, а затем ваш код перечисления выглядит следующим образом:

foreach (Foo foo in someClass.Items) {
    foreach (Bar bar in foo.Items) {
        foreach (Baz baz in bar.Items) {
            yield return baz;
        }
    }
}

// and later in client code

MyEnumerator e = new MyEnumerator(someClass);
foreach (Baz baz in e) {
    if (baz == myValue) {
        // do something useful
        break;
    }
 }

Ответ 10

  • Используйте как PeterAllenWebb, как было предложено.
  • Оберните два для каждого цикла в функцию и верните, когда вы хотите сломать.

Немного искал Google, здесь аналогичный вопрос на форуме MSDN.

Ответ 11

  //describe how to find interesting cells
var query = from row in grid.Rows.OfType<DataGridViewRow>()
            from cell in row.Cells.OfType<DataGridViewCell>()
            where cell.Value == myValue
            select cell;
  //nab the first cell that matches, if any
DataGridViewCell theCell = query.FirstOrDefault();

  //see if we got one
if (theCell != null)
{
  //Do something with theCell
}

Ответ 12

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

Ответ 13

Вы можете изменить свою переменную цикла:

for (int i = 0; i < width; i++)
{
    for (int j = 0; j < height; j++)
    {
        if (NeedToBreak())
        {
            i = width;
            j = height; 
        }
    }

}

Ответ 14

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

private class CustomException : ScriptException
{
  public CustomException()
  {
  }
}

try
{
    foreach(DataGridViewRow row in grid.Rows)
    {
        foreach(DataGridViewCell cell in row.Cells)
        {
            if(cell.Value == myValue)
                throw new CustomException();
        }
    }
}
catch(CustomException)
{ }

Ответ 15

Вот дополнительное решение для цикла for:

bool nextStep = true;
for (int x = 0; x < width && nextStep; x++) {
    for (int y = 0; y < height && nextStep; y++) {
        nextStep = IsBreakConditionFalse(x, y);
    }
}

Ответ 16

int i;
int j;
int flag = 0; // Flag used to break out of the nested loop.
char ballonColor;

if (b == NULL || b->board == NULL) { // Checks for a null board.
    flag = 0;
}
else {
    for (i = 0; i < b->rows && !flag; i++) { // Checks if the board is filled with air (no balloons).
        for (j = 0; j <= b->cols && !flag; j++) {
            if (b->board[i][j] != None) {
                flag = 1;
            }
        }
    }
}

if (flag == 0) {
    return 0;
}
else {
    for (i = 0; i < b->rows && !flag; i++) { //
        for (j = 0; j <= b->cols && !flag; j++) {
            if (b->board[i][j] != None) {
                ballonColor = b->board[i][j];
                if (i == 0) { // Top Row
                    if (j == 0) {
                        if (b->board[i + 1][j] == ballonColor || b->board[i][j + 1] == ballonColor) {
                            return 1;
                        }
                    }
                    else if (j == b->cols) {
                        if (b->board[i + 1][j] == ballonColor || b->board[i][j - 1] == ballonColor) {
                            return 1;
                        }
                    }
                    else {
                        if (b->board[i + 1][j] == ballonColor || b->board[i][j + 1] == ballonColor || b->board[i][j - 1] == ballonColor) {
                            return 1;
                        }
                    }
                }
                else if (i == (b->rows - 1)) { // Bottom Row
                    if (j == 0) {
                        if (b->board[i - 1][j] == ballonColor || b->board[i][j + 1] == ballonColor) {
                            return 1;
                        }
                    }
                    else if (j == b->cols) {
                        if (b->board[i - 1][j] == ballonColor || b->board[i][j - 1] == ballonColor) {
                            return 1;
                        }
                    }
                    else {
                        if (b->board[i - 1][j] == ballonColor || b->board[i][j + 1] == ballonColor || b->board[i][j - 1] == ballonColor) {
                            return 1;
                        }
                    }
                }
                else { // 
                    if (j == 0) {
                        if (b->board[i + 1][j] == ballonColor || b->board[i - 1][j] == ballonColor || b->board[i][j + 1] == ballonColor) {
                            return 1;
                        }
                    }
                    else if (j == b->cols) {
                        if (b->board[i + 1][j] == ballonColor || b->board[i - 1][j] == ballonColor || b->board[i][j - 1] == ballonColor) {
                            return 1;
                        }
                    }
                    else {
                        if (b->board[i + 1][j] == ballonColor || b->board[i - 1][j] == ballonColor || b->board[i][j + 1] == ballonColor || b->board[i][j - 1] == ballonColor) {
                            return 1;
                        }
                    }
                }
            }
        }
    }
}

return 0;

Ответ 17

еще не проверены... но что-то вроде этого может работать

            Action dostuff =  () =>
            {

            }
        foreach (DataGridViewRow row in grid.Rows)
            foreach (DataGridViewCell cell in row.Cells)
                if (cell.Value == myValue)
                    goto A:

        A: dostuff();