JavaScript支持类似goto的语法来打破嵌套循环。一般来说这不是一个好主意,但它被认为是可以接受的做法。 C#不直接支持break labelName
语法...但它确实支持臭名昭着的goto
。
我相信可以在C#中实现等价物:
int i = 0;
while(i <= 10)
{
Debug.WriteLine(i);
i++;
for(int j = 0; j < 3; j++)
if (i > 5)
{
goto Break;//break out of all loops
}
}
Break:
通过JavaScript的相同逻辑,嵌套循环场景是goto
的可接受用法吗?否则,我知道实现此功能的唯一方法是设置具有适当范围的bool
。
答案 0 :(得分:28)
我的观点:嵌套循环的复杂代码流很难推理;分支,无论是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);
故事的寓意:循环以牺牲业务逻辑为代价强调控制流程。不要试图将越来越复杂的控制流堆叠在一起,而是尝试重构代码,以便明确业务逻辑。
答案 1 :(得分:12)
我个人尝试以避免在这里使用goto,只需将循环放入不同的方法 - 虽然你不能轻易突破特定级别的循环,你可以轻松地在任何时候从方法返回。
根据我的经验,这种方法通常会导致更简单,更易读的代码(通常用一个特定的工作)。
答案 2 :(得分:11)
让我们直截了当:使用goto
语句没有任何根本性的错误,它不是邪恶的 - 它只是工具箱中的另一个工具。 你如何使用真的很重要,很容易被滥用。
打破某些描述的嵌套循环可以是对该语句的有效使用,尽管您应该首先查看它是否可以重新设计。您的循环退出表达式可以重写吗?你使用适当类型的循环? 您是否可以过滤您可能要迭代的数据列表,以便您不需要提前退出?您是否应该将某些循环代码重构为单独的函数?
答案 3 :(得分:4)
IMO在不支持break n;
的语言中可以接受n
指定应该突破的循环数。
至少它比设置变量更可读,然后在外循环中检查它。
答案 4 :(得分:2)
我相信'goto'在这种情况下是可以接受的。不幸的是,C#不支持任何突破嵌套循环的好方法。
答案 5 :(得分:2)
这在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;
}
答案 6 :(得分:0)
如果您想跳过这一项,我建议使用continue
;如果您想退出循环,我建议使用break
。对于更深层次的嵌套,将其放在方法中并使用return
。我个人宁愿使用状态bool而不是goto
。而是使用goto
作为最后的手段。
答案 7 :(得分:0)
您几乎总是可以将内部循环破坏到匿名函数或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比布尔/标志,从清晰的角度来看更好,但是这是比任何更加清晰,避免你的同事的嘲弄。
对于高性能情况,goto是合适的,但是只有1%,在这里说实话...
答案 8 :(得分:-1)
int i = 0;
while(i <= 10)
{
Debug.WriteLine(i);
i++;
for(int j = 0; j < 3 && i <= 5; j++)
{
//Whatever you want to do
}
}
答案 9 :(得分:-2)
C#中不可接受。
将循环包装在函数中并使用return
。
编辑:在SO上,downvoting用于不正确的答案,而不是你不同意的答案。由于OP明确要求“它是否可接受?”,回答“不可接受”并非不正确(尽管您可能不同意)。