Как вырваться из 2 циклов без переменной флага в С#?
В качестве тривиального примера можно сказать, что у меня есть следующая сетка, и я ищу конкретное значение ячейки. Когда найдено, мне больше не нужно обрабатывать петли.
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.
}
}
}
Как это делается на С#. В Java я мог бы использовать метку, чтобы назвать самый внешний цикл, а затем разбить этот цикл, но я не могу найти эквивалент в С#.
Каков путь к достижению этого в С#? Я знаю, что могу установить булевский флаг и проверить его во внешнем цикле, чтобы вырваться из него, но он просто кажется слишком подробным.
Спасибо,
Ответы
Ответ 1
Самый приятный способ - разбить второй цикл на функцию, например:
public void DoubleLoop()
{
for(int i = 0; i < width; i++)
{
for(int j = 0; j < height; j++)
{
if(whatever[i][j]) break; // let make this a "double" break
}
}
}
переходит в
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;
}
}
Конечно, не стесняйтесь упрощать это с помощью LINQ или что-то еще (вы можете поставить CheckWhatever
в условие цикла тоже). Это просто многословная демонстрация принципа.
Ответ 2
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
}
Ответ 3
Хотя многие из вышеперечисленных решений верны и отвечают на ваш вопрос, я бы сделал шаг назад и спросил себя: "Есть ли другой способ более четко представлять семантику программы?"
Я хотел бы написать код следующим образом:
var query = from row in grid.Rows
from cell in row.Cells
where cell.Value == myValue
select cell;
if (query.Any())
{
// do something useful;
}
Зачем вообще писать петли? Вы хотите знать, имеет ли конкретная коллекция определенный член, поэтому напишите запрос, задающий этот вопрос, а затем рассмотрите ответ.
Ответ 4
Я бы просто привязал петли к функции и возвратил функцию как способ выхода из цикла для моего решения.
Ответ 5
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: ;
который будет работать, но я бы рекомендовал использовать логический флаг.
EDIT:
Просто добавьте немного больше предупреждений; обычно считается плохой практикой использования goto, поскольку они быстро могут привести к коду спагетти, который (почти) невозможно поддерживать. Говоря это, он был включен в язык С# и доступен для использования, поэтому есть люди, которые считают, что у него есть действующие обычаи. Знайте, что функция существует и используется с большой осторожностью.
Ответ 6
Для полноты также есть неправильный способ:
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
}
Ответ 7
С# имеет инструкцию goto. Фактически, пример в MSDN использует его, чтобы вырваться из дважды вложенного цикла.
Ответ 8
Лучший способ - не делать этого. Шутки в сторону; если вы хотите найти первое появление чего-то в своих вложенных циклах, а затем закончить поиск, то то, что вы хотите сделать, - это НЕ проверять каждый элемент, который явно является тем, что делает конструкция foreach. Я бы рекомендовал использовать регулярный цикл с флагом завершения в инварианте цикла.
Ответ 9
Вы можете написать класс, который реализует 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;
}
}
Ответ 10
- Используйте как PeterAllenWebb, как было предложено.
- Оберните два для каждого цикла в функцию и верните, когда вы хотите сломать.
Немного искал Google, здесь аналогичный вопрос на форуме MSDN.
Ответ 11
//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
}
Ответ 12
Поместите это в функцию и используйте инструкцию return
, когда все будет найдено.
В конце этого значения возвращается нулевое значение, указывающее, что найденный элемент не найден.
Ответ 13
Вы можете изменить свою переменную цикла:
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (NeedToBreak())
{
i = width;
j = height;
}
}
}
Ответ 14
Я думаю, вы можете использовать собственное исключение, например:
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)
{ }
Ответ 15
Вот дополнительное решение для цикла for:
bool nextStep = true;
for (int x = 0; x < width && nextStep; x++) {
for (int y = 0; y < height && nextStep; y++) {
nextStep = IsBreakConditionFalse(x, y);
}
}
Ответ 16
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;
Ответ 17
еще не проверены... но что-то вроде этого может работать
Action dostuff = () =>
{
}
foreach (DataGridViewRow row in grid.Rows)
foreach (DataGridViewCell cell in row.Cells)
if (cell.Value == myValue)
goto A:
A: dostuff();