【问题标题】:How to break out of 2 loops without a flag variable in C#?如何在 C# 中没有标志变量的情况下跳出 2 个循环?
【发布时间】:2009-06-11 17:48:16
【问题描述】:

作为一个简单的例子,假设我有以下网格,我正在寻找一个特定的单元格值。找到后,我不再需要处理循环。

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

这是如何在 C# 中完成的。在 Java 中,我可以使用标签来命名最外层的循环,然后中断该循环,但我似乎在 C# 中找不到等价物。

在 C# 中完成此任务的最简单方法是什么?我知道我可以设置一个布尔标志,并在外循环中检查它以跳出那个标志,但这似乎太冗长了。

谢谢,

【问题讨论】:

  • 这个例子有一些有用的反建议,但我不认为一般问题总是可以愉快地重组掉。例如。我来到这里是因为我的小解析器方法在 while 循环内有一个开关(状态)。当然,如果我将所有 switch 案例转换为 if...else if,我可以使用 break,但我宁愿不这样做。将开关移动到函数中是可行的,但需要传递大量参数(并且会降低性能?)。我捏着鼻子跟着goto走了。我认为布尔标志(加上继续进入 while 条件?)将是另一个最佳选择。

标签: c# syntax


【解决方案1】:

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
}

【讨论】:

  • +1 表示转到。今天的人们害怕 goto,但这是对 goto 的合法使用。
  • 我犯了一个错误,因为一个类似的问题提到了 goto 并且被否决了。但是使用 break 关键字与在循环结束时调用 goto 几乎是一样的,不是吗?
  • @Meta-Knight:当您使用它时,函数的多次返回等效于 goto。它只是不那么混乱,因为它施加了你不能向上 goto 或跳过 goto 标签的限制(这两种类型都会迅速退化为混乱)
  • @Jimmy 每个控制结构都相当于一个 goto。同样,您用 Python、Ruby、C# 或 Lisp 编写的每个程序都可以完全复制为汇编代码或原始机器代码。语言是弥合计算硬件和人类开发人员之间差距的结构,重要的是要认识到从函数返回值是一种设计模式,即使它们共享实现,它在使用方式上的含义也与简单的 goto 有很大不同引擎盖下的相似之处。
  • 我认为 Jimmy 的声明是一种消极的部分等价,因为很难阅读充斥着大量返回语句的方法流。我不确定我是否完全同意(有时提前返回似乎不如将方法的其余部分包装在另一个 if 块中),但总的来说,我认为避免多次返回是一个好习惯。其他控制结构不一定与消极意义上的 goto 相似。
【解决方案2】:

虽然上面的许多解决方案都是正确的并回答了你的问题,但我会退后一步问自己“还有其他方法可以更清楚地表示程序的语义吗?” p>

我倾向于这样写代码:

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

为什么要编写任何循环?您想知道特定集合是否有特定成员,因此编写一个询问该问题的查询,然后检查答案。

【讨论】:

  • 但在这种情况下,我会检查所有项目。这不好,因为我只需要第一个。
  • @RredCat:对不起,我不明白你的反对意见。您的反对意见(1)是否需要第一个匹配的项目,而不是是否存在这样的项目?如果是这样,则使用“FirstOrDefault”,而不是“Any”。或者(2)即使第一个有效,“任何”也会检查每个项目?你为什么相信“Any”能做到这一点?当它得到一个有效的时候它就会停止。
  • 2埃里克。你是对的,我忘了'Any()'。 Linq 按请求执行(不是在初始化时) - 我错过了。
  • 对!别人为你写的循环,为什么还要写循环呢!
【解决方案3】:

最令人愉快的方法是将第二个循环分解成一个函数,如下所示:

public void DoubleLoop()
{
    for(int i = 0; i < width; i++)
    {
        for(int j = 0; j < height; j++)
        {
            if(whatever[i][j]) break; // let's 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 个函数,每个函数只包含一个循环。
  • 我想说最简单的解决方案是 Eric 的 LINQ 解决方案。但是,假设我们在 .NET 2 上并且没有 LINQ,我认为将整个 OP 的原始示例移到另一种方法中并使用 return 跳出循环比拆分循环要干净得多超过两种方法,因为这两种方法最终会掩盖意图。
  • 这到底怎么比 goto 好?真是一场噩梦。
【解决方案4】:

我只是将循环包装到一个函数中,并让函数返回作为我的解决方案退出循环的一种方式。

【讨论】:

  • 比我快几秒钟,但我写了一个很好的例子,所以我的留下了!
  • 为什么投反对票?他只是在说 mquander 的建议,但没有代码示例。
  • 这种方法并不总是完全可行的,特别是如果您需要在循环中对您的细胞进行一组复杂的评估,这取决于您方法中的其他内容。
  • 我想我会把编写代码作为提出问题的人的练习。 :)
  • @McWafflestix 这种方法非常可取。如果你开始纠结代码,这表明你做错了事。易于理解的干净代码通常会有更少的缺陷,因为它更容易测试并且更容易看出何时出错。
【解决方案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: ;

这会起作用,但我建议使用布尔标志。

编辑: 只是在这里添加更多警告;通常认为使用 goto 是不好的做法,因为它们很快会导致(几乎)无法维护的意大利面条代码。话虽如此,它已包含在 C# 语言中,并且可以使用,因此很明显有些人认为它具有有效的用法。知道该功能的存在并谨慎使用。

【讨论】:

  • 即使我自己从来没有使用过 goto,我也想知道同样的事情...... PeterAllenWebb 的回答基本相同,获得了 2 票赞成。这实际上也是一个更好的答案(就提供使用示例而言)。奇数。
  • 使用过多的布尔标志会导致代码不可读和不可维护。有更多的石头要扔吗?
【解决方案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】:

C#does have a goto statement。事实上,MSDN 上的示例就是用它来打破双重嵌套循环的。

【讨论】:

  • 我会建议这个,直到我想起 xkcd.com/292 但是是的,goto 可以解决问题。
  • 它在 MSDN 中使用的事实并不意味着它是最好的做事方式(就像那里的许多其他例子一样),只要你能看到这个问题,我仍然在避免 goto可以通过其他方式解决(主要是将逻辑提取到单独的函数中,然后返回函数)
  • @zappan:当然问题可以通过其他方式解决。但是使用goto 是最干净的方式。
【解决方案8】:

最好的方法是不要这样做。严重地;如果你想在嵌套循环中找到第一次出现的东西,然后完成查找,那么你要做的不是检查每个元素,这正是 foreach 构造所做的。我建议在循环不变量中使用带有终止标志的常规 for 循环。

【讨论】:

    【解决方案9】:

    您可以编写一个在一般情况下实现 IEnumerator 的类,然后您的枚举代码如下所示:

    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】:

      这里是 for 循环的另一个解决方案:

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

      【讨论】:

      • 不错的解决方案!对于避免使用 goto 的人来说,可读且易于理解。
      【解决方案11】:
      1. 按照 PeterAllenWebb 的建议使用 go to。
      2. 将两个for每个循环包装成一个函数,并在需要中断时返回。

      用谷歌搜索了一下,这里是 MSDN 论坛上的a similar question

      【讨论】:

        【解决方案12】:
          //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
        }
        

        【讨论】:

          【解决方案13】:

          将其放入函数中并在找到内容时使用return 语句。
          最后返回一个空值 - 表示未找到搜索项。

          【讨论】:

            【解决方案14】:

            你可以修改你的循环变量:

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

            【讨论】:

            • 我讨厌这种方式。维护非常困难。此外,您不能将其与 foreach 一起使用!
            【解决方案15】:

            尚未测试...但类似的方法可能有效

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

            【讨论】:

            • 嗯,我想你最好先测试一下。不过,不错的初级尝试。
            【解决方案16】:

            我认为您可以使用自定义异常,例如:

            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)
            { }
            

            【讨论】:

              【解决方案17】:
              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;
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2017-10-25
                • 2015-12-10
                • 1970-01-01
                • 1970-01-01
                • 2020-02-29
                • 2014-02-01
                • 2014-09-21
                • 1970-01-01
                相关资源
                最近更新 更多