是否可以打破嵌套循环?

JavaScript支持类似goto的语法来突破嵌套循环。 一般来说这不是一个好主意,但它被认为是可以接受的做法。 C#不直接支持break labelName语法…但它确实支持臭名昭着的goto

我相信在C#中可以达到相同的效果:

  int i = 0; while(i <= 10) { Debug.WriteLine(i); i++; for(int j = 0; j  5) { goto Break;//break out of all loops } } Break: 

通过JavaScript的相同逻辑,嵌套循环场景是goto可接受的用法吗? 否则,我知道实现此function的唯一方法是设置具有适当范围的bool

我的观点:使用嵌套循环的复杂代码流很难推理; 分支,无论是goto还是break,只会让它变得更难。 我不会写goto,而是首先想到是否有办法消除嵌套循环。

一些有用的技巧:

第一种技术:将内循环重构为方法。 让方法返回是否突破外循环。 所以:

 for(outer blah blah blah) { for(inner blah blah blah) { if (whatever) { goto leaveloop; } } } leaveloop: ... 

 for(outer blah blah blah) { if (Inner(blah blah blah)) break; } ... bool Inner(blah blah blah) { for(inner blah blah blah) { if (whatever) { return true; } } return false; } 

第二种技术:如果循环没有副作用,请使用LINQ。

 // fulfill the first unfulfilled order over $100 foreach(var customer in customers) { foreach(var order in customer.Orders) { if (!order.Filled && order.Total >= 100.00m) { Fill(order); goto leaveloop; } } } leaveloop: 

相反,写:

 var orders = from customer in customers from order in customer.Orders; where !order.Filled where order.Total >= 100.00m select order; var orderToFill = orders.FirstOrDefault(); if (orderToFill != null) Fill(orderToFill); 

没有循环,因此不需要突破。

或者,正如配置程序在注释中指出的那样,您可以使用以下格式编写代码:

 var orderToFill = customers .SelectMany(customer=>customer.Orders) .Where(order=>!order.Filled) .Where(order=>order.Total >= 100.00m) .FirstOrDefault(); if (orderToFill != null) Fill(orderToFill); 

故事的寓意: 循环强调控制流程而牺牲业务逻辑 。 不要试图将越来越复杂的控制流堆叠在一起,而是尝试重构代码,以便明确业务逻辑。

我个人试着通过简单地将循环放入不同的方法来避免使用goto – 虽然你不能轻易地突破特定级别的循环,但你可以在任何时候轻松地从方法返回

根据我的经验,这种方法通常会导致更简单,更易读的代码和更短的方法(做一个特定的工作)。

让我们直截了当:使用goto语句没有任何根本性的错误,它不是邪恶的 – 它只是工具箱中的另一个工具。 它是如何使用它真正重要的,它很容易被滥用。

打破某些描述的嵌套循环可能是该语句的有效用法,尽管您应该首先查看它是否可以重新设计。 您的循环退出表达式可以重写吗? 你使用适当类型的循环? 您是否可以过滤可能迭代的数据列表,以便您不需要提前退出? 你应该将一些循环代码重构为一个单独的函数吗?

IMO在不支持break n;语言中是可以接受的break n; 其中n指定应该突破的循环数。
至少它比设置变量更可读,然后在外循环中检查变量。

我相信’goto’在这种情况下是可以接受的。 不幸的是,C#不支持任何突破嵌套循环的好方法。

这在C#中是一种不可接受的做法。 如果您的设计无法避免它,那么,必须使用它。 但首先要耗尽所有其他选择。 它将提高可读性和可维护性。 就你的例子而言,我已经制定了一个这样的潜在重构:

 void Original() { int i = 0; while(i <= 10) { Debug.WriteLine(i); i++; if (Process(i)) { break; } } } bool Process(int i) { for(int j = 0; j < 3; j++) if (i > 5) { return true; } return false; } 
 int i = 0; while(i <= 10) { Debug.WriteLine(i); i++; for(int j = 0; j < 3 && i <= 5; j++) { //Whatever you want to do } } 

如果你想跳过那一项,我建议使用continue ,如果你想退出循环,我建议使用break 。 对于更深层次的嵌套,将它放在一个方法中并使用return 。 我个人宁愿使用状态bool而不是goto 。 而是使用goto作为最后的手段。

匿名函数

你几乎总能将内循环破坏为匿名函数或lambda。 在这里你可以看到函数曾经是一个内部循环,我将不得不使用GoTo。

 private void CopyFormPropertiesAndValues() { MergeOperationsContext context = new MergeOperationsContext() { GroupRoot = _groupRoot, FormMerged = MergedItem }; // set up filter functions caller var CheckFilters = (string key, string value) => { foreach (var FieldFilter in MergeOperationsFieldFilters) { if (!FieldFilter(key, value, context)) return false; } return true; }; // Copy values from form to FormMerged foreach (var key in _form.ValueList.Keys) { var MyValue = _form.ValueList(key); if (CheckFilters(key, MyValue)) MergedItem.ValueList(key) = MyValue; } } 

在手动搜索数据集中的多个项目时也经常发生这种情况。 遗憾的是,从清晰的角度来看,正确使用goto比Booleans / flags要好,但这比其中任何一个都要清楚,并且避免了同事的嘲讽。

对于高性能情况,goto会适合,但只有1%,让我们说实话……

在C#中是不可接受的。

只需将循环包装在函数中并使用return

编辑:在SO上,downvoting用于不正确的答案,而不是你不同意的答案。 由于OP明确要求“它是否可以接受?”,回答“不可接受”并不正确(尽管你可能不同意)。