web-dev-qa-db-fra.com

Comment sortir de 2 boucles sans variable drapeau en C #?

Comme exemple trivial, disons que j'ai la grille suivante et que je recherche une valeur de cellule particulière. Une fois trouvé, je n'ai plus besoin de traiter les boucles. 

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

Comment cela se fait en C #. En Java, je pourrais utiliser une étiquette pour nommer la boucle la plus externe, puis la casser, mais je n'arrive pas à trouver l'équivalent dans C #.

Quelle est la meilleure façon d’accomplir cela en c #? Je sais que je peux définir un drapeau booléen et le vérifier dans la boucle extérieure pour sortir de celui-ci également, mais cela me semble trop verbeux.

Merci,

38
Matthew Vines

Le moyen le plus agréable est de décomposer la deuxième boucle en une fonction, comme ceci:

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

va à

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

Bien sûr, n'hésitez pas à simplifier ceci avec LINQ ou autre chose (vous pouvez également mettre CheckWhatever dans la condition de boucle.) Ceci n'est qu'une démonstration verbeuse du principe.

35
mquander

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

Bien que beaucoup des solutions ci-dessus soient correctes et répondent à votre question, je voudrais prendre un peu de recul et me demander "existe-t-il un autre moyen de représenter plus clairement la sémantique du programme?"

Je serais enclin à écrire le code comme ceci:

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

Pourquoi écrire des boucles du tout? Vous voulez savoir si une collection particulière a un membre particulier, écrivez donc une requête qui pose cette question, puis examinez la réponse.

57
Eric Lippert

Je voudrais juste envelopper les boucles dans une fonction et avoir la fonction return comme un moyen de sortir les boucles de ma solution.

23
JB King
        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: ;

cela fonctionnera, mais je recommanderais l’utilisation d’un drapeau booléen.

EDIT: Juste pour ajouter un peu plus d’avertissement ici; Il est généralement considéré comme une mauvaise pratique d’utiliser des goto car ils peuvent rapidement conduire à un code spaghetti impossible à (presque) conserver. Cela étant dit, il était inclus dans le langage C # et pouvait être utilisé. Il est donc clair que certaines personnes pensent qu'il a des utilisations valables. Sachez que la fonctionnalité existe et utilisez-la avec prudence.

19
Timothy Carter

Pour être complet, il y a aussi la mauvaise façon de le faire:

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

C # a une déclaration goto . En fait, l'exemple sur MSDN l'utilise pour sortir d'une boucle doublement imbriquée.

11
PeterAllenWebb

Le meilleur moyen est de ne pas le faire. Sérieusement; si vous voulez trouver la première occurrence de quelque chose dans vos boucles imbriquées, puis terminer la recherche, vous ne devez PAS examiner chaque élément, ce qui est explicitement ce que fait la construction foreach. Je recommanderais d'utiliser une boucle for régulière avec un indicateur de terminaison dans l'invariant de boucle.

8
Paul Sonier

Vous pouvez écrire une classe qui implémente IEnumerator <T> dans le cas général, puis votre code d’énumération se présente comme suit:

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;
    }
 }
5
plinth
  1. Utilisez go to comme PeterAllenWebb comme suggéré.
  2. Enveloppez les deux pour chaque boucle dans une fonction et revenez quand vous voulez faire une pause.

Avez un peu de recherche google, voici une question similaire sur le forum MSDN.

2
J.W.
  //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
}
2
Amy B

Mettez cela dans une fonction et utilisez une instruction return, lorsque des objets sont trouvés.
À la fin de celui-ci, une valeur NULL est renvoyée - indiquant que l'élément recherché est introuvable.

1
shahkalpesh

je pense que vous pouvez utiliser une exception personnalisée, par exemple:

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
Bondaryuk Vladimir

Voici une solution supplémentaire pour la boucle for:

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

Vous pouvez modifier votre variable de boucle:

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

}
0
Alan Jackson
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
Crawley Griffinth