55

As a trivial example lets say I have the following grid and I am looking for a particular cells value. When found I no longer need to process the loops.

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

How is this done in C#. In Java I could use a label to name the outermost loop, and then break that loop, but I can't seem to find an equivelant in C#.

What is the tersest way of accomplishing this in c#? I know I can set a boolean flag, and check it in the outer loop to break out of that one as well, but it just seems too verbose.

Thanks,

Matthew Vines
  • 27,253
  • 7
  • 76
  • 97
  • There've been some useful counter-suggestions for this example, but I don't think that the general question can always be restructured away happily. E.g. I came here because my little parser method has a switch (state) inside of a while loop. Sure, I could use break if I converted all the switch cases to if...else if, but I'd much rather not. Moving the switch into a function works but requires passing lots of parameters (and diminishing performance?). I held my nose and went with goto. I think a boolean flag (plus continue to go to the while condition?) would be the other best option. – Jon Coombs Sep 29 '13 at 02:07

17 Answers17

92

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
}
Ivan Castellanos
  • 8,041
  • 1
  • 47
  • 42
Jimmy
  • 89,068
  • 17
  • 119
  • 137
  • 77
    +1 for goto. People today are scared of goto, but this is a legitimate use of one. – rlbond Jun 11 '09 at 18:13
  • 8
    I made the mistake of mentioning goto for a similar problem and got downvotes. But using the break keyword is pretty much the same thing as calling a goto to the end of the loop, no? – Meta-Knight Jun 11 '09 at 18:31
  • 2
    @Meta-Knight: while you're at it, multiple returns from a function is equivalent to a goto. its just less messy because it imposes the restriction that you can't do upwards goto, or jump over a goto label (both types degenerate quickly into chaos) – Jimmy Jun 11 '09 at 19:06
  • 2
    @Jimmy every control structure is equivalent to a goto. Similarly, every program you write in Python, Ruby, C#, or Lisp can be duplicated exactly in assembly code or raw machine code. Languages are constructs which bridge the gap between computing hardware and human developers, it's important to recognize that returning a value from a function is a design pattern which has much different implications in how it will be used than a simple goto, even if they share implementation similarities under the hood. – Wedge Jun 11 '09 at 21:10
  • 3
    I think Jimmy's claim was one of partial equivalence in a negative sense, in that it's hard to read the flow of methods riddled with lots of return statements. I'm not sure that I agree completely (sometimes an early return seems less cluttered than wrapping the rest of the method in another if block), but in general I think avoiding multiple returns is a good practice. Other control structures wouldn't necessarily resemble goto in a negative sense. – Jon Coombs Sep 29 '13 at 02:16
61

Though many of the solutions above are correct and answer your question, I would take a step back and ask myself "is there another way to more clearly represent the semantics of the program?"

I would be inclined to write the code like this:

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

Why write any loops at all? You want to know if a particular collection has a particular member, so write a query that asks that question, and then examine the answer.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • But in this case I check all items. It isn't good because I need first one only. – RredCat Jun 28 '11 at 07:54
  • 11
    @RredCat: I'm sorry, I don't understand your objection. Is your objection (1) that you need the first item that matches, not whether there is such an item or not? If so, then use "FirstOrDefault", rather than "Any". Or (2) that "Any" checks every item even if the first one works? Why do you believe that "Any" does that? It stops when it gets one that works. – Eric Lippert Jun 28 '11 at 13:41
  • 2Eric. You are right, I forgot about 'Any()'. Linq executes by request (not in initialization time) - I missed it. – RredCat Jun 29 '11 at 13:48
  • Right! Why write a loop when somebody else has written it for you! – Suncat2000 Mar 12 '20 at 19:06
39

The most pleasant way is to break the second loop out into a function, like this:

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

goes to

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

Of course, feel free to simplify this with LINQ or whatever (you could put CheckWhatever into the loop condition, too.) This is just a verbose demonstration of the principle.

mqp
  • 70,359
  • 14
  • 95
  • 123
  • Gets the accepted flag for adding in the example. Thanks all who responded. – Matthew Vines Jun 11 '09 at 17:58
  • 1
    This is slightly different from my answer which was that you could keep both loops rolled into one function where here there are 2 functions, each containing just one loop. – JB King Jun 12 '09 at 13:37
  • I would say the easiest solution is Eric's LINQ one. However, assuming we're on .NET 2 and don't have LINQ, I would argue that moving the whole of the OP's original example into another method and using `return` to break out of the loops is much cleaner than splitting the loop over two methods as the two methods end up obscuring the intent. – Damian Powell Sep 06 '11 at 10:49
  • How on earth is this better than goto? What an absolute nightmare. – Thorham May 23 '20 at 12:50
23

I'd just wrap the loops into a function and have the function return as a way to exit the loops for my solution.

JB King
  • 11,860
  • 4
  • 38
  • 49
  • 1
    Beat me by a few seconds, but I wrote a nice example, so mine's staying! – mqp Jun 11 '09 at 17:53
  • 3
    Why the downvote? He is just saying what mquander suggests, but without a code example. – Matthew Vines Jun 11 '09 at 17:54
  • 2
    this sort of method is not always completely viable, particularly if you have a complicated set of evaluations on your cells to do in the loop dependent upon other things in your method. – Paul Sonier Jun 11 '09 at 17:56
  • 4
    I thought I'd leave writing the code as an exercise for the person posing the question. :) – JB King Jun 11 '09 at 17:57
  • @McWafflestix this method is vastly preferable though. If you start getting tangled up code, that's an indication that you're doing something the wrong way. Clean code that's understandable will have fewer defects, generally, because it's easier to test and because it's easier to see when it's wrong. – Wedge Jun 11 '09 at 21:06
21
        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: ;

that will work, but I would recommend using a boolean flag.

EDIT: Just to add a little more warning here; it is generally considered bad practice to use goto's as they quickly can lead to spaghetti code that is (nearly) impossible to maintain. That being said, it was included in the C# language, and is available for use, so clearly there are people who believe it has valid usages. Know that the feature exists and use with great caution.

Timothy Carter
  • 15,459
  • 7
  • 44
  • 62
  • 1
    Even though I'd never use a goto myself, I was wondering the same thing... The answer by PeterAllenWebb got 2 upvotes for basically the same answer. This one's actually a better answer too (in terms of providing a usage example). Odd. – Mark Carpenter Jun 11 '09 at 17:59
  • 2
    Using too many boolean flags can lead to unreadable and unmaintainable code. Got more stones to throw? – Suncat2000 Mar 12 '20 at 19:08
15

For completeness, there's also the wrong way to do it:

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
}
Eclipse
  • 44,851
  • 20
  • 112
  • 171
13

C# does have a goto statement. In fact, the example on MSDN uses it to break out of a doubly-nested loop.

PeterAllenWebb
  • 10,319
  • 3
  • 37
  • 44
  • 7
    I was going to suggest this until I remembered http://xkcd.com/292/ But yes, a goto could solve the problem. – Chet Jun 11 '09 at 17:53
  • 2
    the fact it's used in MSDN doesn't mean it's the best way of doing things (as many other examples there), i'm still at the avoid gotos whenever you can point of view, this problem can be solved in other ways (primarily extracting the logic into the separate function and returning the function) – zappan Jun 11 '09 at 17:58
  • 1
    @zappan: Certainly the problem can be solved other ways. But using a `goto` is the cleanest way. – Suncat2000 Mar 12 '20 at 19:11
8

The best way is to not do this. Seriously; if you want to find the first occurrence of something in your nested loops, and then finish looking, then what you want to do is NOT to examine each element, which is explicitly just what the foreach construct does. I'd recommend using a regular for loop with a termination flag in the loop invariant.

Paul Sonier
  • 38,903
  • 3
  • 77
  • 117
5

Here is an additional solution for the for-loop:

bool nextStep = true;
for (int x = 0; x < width && nextStep; x++) {
    for (int y = 0; y < height && nextStep; y++) {
        nextStep = IsBreakConditionFalse(x, y);
    }
}
d3rbastl3r
  • 463
  • 1
  • 6
  • 16
5

You can write a class that implements IEnumerator<T> in the general case and then your enumeration code looks like this:

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;
    }
 }
plinth
  • 48,267
  • 11
  • 78
  • 120
2
  1. Use go to as PeterAllenWebb as suggested.
  2. Wrap the two for each loop into a function, and return when you want to break.

Did a bit google search, here is a similar question on MSDN forum.

J.W.
  • 17,991
  • 7
  • 43
  • 76
2
  //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
}
Amy B
  • 108,202
  • 21
  • 135
  • 185
1

Put that into a function & use a return statement, when things are found.
At the end of it return a null value - indicating searched item not found.

shahkalpesh
  • 33,172
  • 3
  • 63
  • 88
0

haven't tested yet... but something like this might work

            Action dostuff =  () =>
            {

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

        A: dostuff();
skyler
  • 11
0

i think you can use custom exception for example:

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

You could modify your loop variable:

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

}
Alan Jackson
  • 6,361
  • 2
  • 31
  • 32