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,
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.
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
}
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.
Je voudrais juste envelopper les boucles dans une fonction et avoir la fonction return comme un moyen de sortir les boucles de ma solution.
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.
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
}
C # a une déclaration goto . En fait, l'exemple sur MSDN l'utilise pour sortir d'une boucle doublement imbriquée.
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.
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;
}
}
Avez un peu de recherche google, voici une question similaire sur le forum MSDN.
//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
}
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.
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)
{ }
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);
}
}
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;
}
}
}
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;