如何在C#中没有标志变量的情况下突破2个循环?

Mat*_*nes 42 c# syntax

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

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.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是如何在C#中完成的.在Java中,我可以使用标签来命名最外层循环,然后打破该循环,但我似乎无法在C#中找到equivelant.

在c#中实现这一目标的最简洁方法是什么?我知道我可以设置一个布尔标志,并在外部循环中检查它以打破那个,但它似乎太冗长了.

谢谢,

Jim*_*mmy 66

1

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

2

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

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
}
Run Code Online (Sandbox Code Playgroud)

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

Eri*_*ert 57

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

我倾向于写这样的代码:

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

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

  • @RredCat:对不起,我不明白你的反对意见.您是否反对(1)您需要匹配的第一个项目,而不是是否有这样的项目?如果是这样,那么使用"FirstOrDefault",而不是"Any".或者(2)"Any"检查每个项目,即使第一个项目有效?为什么你相信"任何"这样做?当它得到一个有效的时候停止. (10认同)
  • 现在那很光滑!+1 (3认同)

mqp*_*mqp 36

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

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,随意使用LINQ或其他任何东西来简化它(你也可以放入CheckWhatever循环条件.)这只是原理的详细演示.


JB *_*ing 23

我只是将循环包装到一个函数中,并将函数返回作为退出循环的方法来解决我的问题.

  • 我以为我会把代码写成为提出问题的人的练习.:) (4认同)
  • 为什么投反对票?他只是说了 mquander 的建议,但没有代码示例。 (3认同)
  • 几秒钟打败我. (2认同)
  • 这种方法并不总是完全可行,特别是如果您需要在循环中根据方法中的其他内容对单元格进行一组复杂的评估。 (2认同)

Tim*_*ter 19

        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: ;
Run Code Online (Sandbox Code Playgroud)

这将工作,但我建议使用布尔标志.

编辑:只是在这里添加一点警告; 一般认为使用goto是不好的做法,因为它们很快就会导致(几乎)无法维护的意大利面条代码.话虽这么说,它被包含在C#语言中,并且可以使用,所以显然有些人认为它具有有效的用法.知道该功能存在并非常谨慎使用.

  • goto使用不当很糟糕,这显然不是. (3认同)
  • 使用太多布尔标志可能会导致代码不可读且难以维护。还有更多石头要扔吗? (2认同)

Ecl*_*pse 14

为了完整起见,还有错误的方法:

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
}
Run Code Online (Sandbox Code Playgroud)


Pet*_*ebb 11

C#确实有一个goto语句.实际上,MSDN上的示例使用它来打破双嵌套循环.

  • 在我记得http://xkcd.com/292/之前我会建议这一点.但是,是的,goto可以解决这个问题. (6认同)
  • 它在MSDN中使用的事实并不意味着它是最好的做事方式(那里有很多其他的例子),无论什么时候你都能看到这个问题,我仍然处于避免的状态,这个问题可以通过其他方式解决(主要是将逻辑提取到单独的函数并返回函数) (2认同)

Pau*_*ier 8

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


d3r*_*l3r 6

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

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

  • 很好的解决方案!对于避免使用 goto 的人来说是可读且易于理解的。 (2认同)

pli*_*nth 5

您可以编写一个在一般情况下实现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;
    }
 }
Run Code Online (Sandbox Code Playgroud)