uso - ¿Cómo salir de 2 bucles sin una variable de bandera en C#?
uso de flags (17)
- Use ir a como PeterAllenWebb como se sugiere.
- Envuelva los dos para cada ciclo en una función, y regrese cuando quiera romperlos.
Hice un poco de búsqueda en Google, aquí hay una pregunta similar en el foro de MSDN.
Como ejemplo trivial, digamos que tengo la siguiente grilla y estoy buscando un valor de celdas particular. Cuando me encuentro, ya no necesito procesar los bucles.
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ómo se hace esto en C #. En Java podría usar una etiqueta para nombrar el bucle más externo, y luego romper ese bucle, pero parece que no puedo encontrar un equivalente en C #.
¿Cuál es la forma más sencilla de lograr esto en c #? Sé que puedo establecer un indicador booleano y verificarlo en el bucle externo para salir también de ese, pero parece demasiado detallado.
Gracias,
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
}
Aquí hay una solución adicional para el 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);
}
}
Aunque muchas de las soluciones anteriores son correctas y responden a su pregunta, daré un paso atrás y me preguntaría "¿hay alguna otra forma de representar más claramente la semántica del programa?"
Me inclinaría a escribir el código de esta manera:
var query = from row in grid.Rows
from cell in row.Cells
where cell.Value == myValue
select cell;
if (query.Any())
{
// do something useful;
}
¿Por qué escribir cualquier bucle? Desea saber si una colección en particular tiene un miembro en particular, entonces escriba una consulta que haga esa pregunta, y luego examine la respuesta.
C # tiene una declaración goto . De hecho, el ejemplo en MSDN lo usa para salir de un bucle doblemente anidado.
Creo que puedes usar una excepción personalizada, por ejemplo:
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)
{ }
La forma más agradable es dividir el segundo bucle en una función, como esta:
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 a
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;
}
}
Por supuesto, siéntase libre de simplificar esto con LINQ o lo que sea (también podría poner CheckWhatever
en la condición de bucle). Esto es solo una demostración detallada del principio.
La mejor manera es no hacer esto. Seriamente; si desea encontrar la primera aparición de algo en sus bucles anidados, y luego terminar de buscar, entonces lo que quiere hacer es NO examinar cada elemento, que es explícitamente lo que hace el constructo foreach. Yo recomendaría usar un bucle for para siempre con un indicador de terminación en el bucle invariante.
Me gustaría envolver los bucles en una función y devolver la función como una forma de salir de los bucles de mi solución.
Póngalo en una función y use una declaración de return
, cuando se encuentren cosas.
Al final de este, devuelve un valor nulo, que indica que no se encontró el elemento buscado.
Para completar, también existe la forma incorrecta de hacerlo:
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
}
Podrías modificar tu variable de bucle:
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (NeedToBreak())
{
i = width;
j = height;
}
}
}
Puede escribir una clase que implemente IEnumerator <T> en el caso general y luego su código de enumeración se verá así:
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;
}
}
aún no lo han probado ... pero algo así podría funcionar
Action dostuff = () =>
{
}
foreach (DataGridViewRow row in grid.Rows)
foreach (DataGridViewCell cell in row.Cells)
if (cell.Value == myValue)
goto A:
A: dostuff();
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: ;
eso funcionará, pero recomendaría usar una bandera booleana.
EDITAR: solo para agregar un poco más de advertencia aquí; En general, se considera una mala práctica utilizar goto ya que pueden conducir rápidamente a un código de spaghetti que es (casi) imposible de mantener. Dicho esto, se incluyó en el lenguaje C # y está disponible para su uso, por lo que es evidente que hay personas que creen que tiene usos válidos. Sepa que la característica existe y use con mucha precaución.
//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
}
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;