Ras*_*org 50 c# loops goto break
在做了一些关于如何突破二次循环的研究之后
while (true) { // Main Loop
for (int I = 0; I < 15; I++) { // Secondary loop
// Do Something
break; // Break main loop?
}
}
Run Code Online (Sandbox Code Playgroud)
大多数人建议调用'goto'函数
看如下例:
while (true) { // Main Loop
for (int I = 0; I < 15; I++) { // Secondary Loop
// Do Something
goto ContinueOn; // Breaks the main loop
}
}
ContinueOn:
Run Code Online (Sandbox Code Playgroud)
然而; 我经常听说'goto'声明是不好的做法.下图完美地说明了我的观点:
Jon*_*eet 49
编辑:
goto语句真的有多糟糕,为什么?
这取决于具体情况.我记不起任何时候我发现它使代码比重构更具可读性.它还取决于你个人对可读性的看法 - 有些人比其他人更不喜欢它,从其他答案可以清楚地看出.(作为一个兴趣点,它被广泛用于生成的代码中--C#5中的所有异步/等待代码都基于有效的许多gotos).
问题在于goto
倾向于使用的情况往往是重构帮助事物的情况 - 而goto
坚持使用解决方案随着代码变得更加复杂而变得更难以遵循.
是否有一种比使用'goto'语句更有效的方法来打破主循环?
绝对.将您的方法提取到一个单独的函数中:
while (ProcessValues(...))
{
// Body left deliberately empty
}
...
private bool ProcessValues()
{
for (int i = 0; i < 15; i++)
{
// Do something
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
我通常更喜欢这样做而不是引入一个额外的局部变量来跟踪"我已经完成" - 当然,这会起作用.
MgS*_*Sam 36
我将非常不同意这里的所有其他答案.您使用的代码goto
没有任何问题.C#有一个声明的原因goto
,正是您所描述的这些类型的场景.
goto
只是有一个负面的耻辱,因为在20世纪70年代和以前的人会写出可怕的,完全不可维护的代码,其中控制流量遍布整个地方因为goto
.C#goto
甚至不允许在方法之间转换!然而,仍有这种不合理的耻辱.
在我看来,使用"现代" goto
打破内循环绝对没有错.人们提供的"替代品"总是变得更加复杂和难以阅读.
通常认为方法是可重用的.为循环的内部部分创建一个完全独立的方法,只能从该位置调用,并且方法实现可能最终位于源代码中的某个远程位置,这不是一种改进.
要避免这种愚蠢的运动goto
基本上等同于政治正确性,但在编程世界中.
Esa*_*ija 34
goto语句真的有多糟糕,为什么?
由于给出的所有正常原因,这真的很糟糕.在不支持它们的语言中模拟标记循环时,它是完美的.
在许多情况下,用函数替换它会分散真正应该作为相同单元读取的逻辑.这使得阅读更加困难.没有人喜欢在行程结束之前跟踪一些没有做任何事情的功能,当你有点忘记你从哪里开始时.
用布尔值和一堆额外的ifs和休息替换它真的很笨重,并且更难以遵循真实的意图,就像任何噪音一样.
在java(和javascript)中,这是完全可以接受的(标记为循环):
outer: while( true ) {
for( int i = 0; i < 15; ++i ) {
break outer;
}
}
Run Code Online (Sandbox Code Playgroud)
在C#中,看起来非常接近的等价物不是:
while( true ) {
for (int I = 0; I < 15; I++) {
goto outer;
}
}
outer:;
Run Code Online (Sandbox Code Playgroud)
因为这个词goto
具有心理效应,使人们放弃所有常识,并使他们无论上下文如何链接xkcd.
是否有一种比使用'goto'语句更有效的方法来打破主循环?
在某些情况下没有,这就是其他语言提供标记循环和C#提供的原因goto
.请注意,您的示例过于简单,并且使得解决方案看起来不太糟糕,因为它们是根据示例进行定制的.事实上,我也可以这样建议:
for (int I = 0; I < 15; I++) {
break;
}
Run Code Online (Sandbox Code Playgroud)
这个怎么样:
int len = 256;
int val = 65536;
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len; j++)
{
if (i + j >= 2 * val)
{
goto outer;
}
val = val / 2;
}
}
outer:;
Run Code Online (Sandbox Code Playgroud)
这对你来说仍然很好看:
int len = 256;
int val = 65536;
for (int i = 0; i < len; i++)
{
if (!Inner(i, ref val, len))
{
break;
}
}
private bool Inner(int i, ref int val, int len)
{
for (int j = 0; j < len; j++)
{
if (i + j >= 2 * val)
{
return false;
}
val = val / 2;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
我的一个同事(在固件编程方面有 15 年以上的经验)和我一直使用 goto。但是,我们只用它来处理异常!!例如:
if (setsockopt(sd,...)<0){
goto setsockopt_failed;
}
...
setsockopt_failed:
close(sd);
Run Code Online (Sandbox Code Playgroud)
据我所知,这是在 C 中处理异常的最佳方式。我相信,我也为 C# 工作。此外,我不是唯一一个这么认为的人:C 或 C++ 中好的 goto 示例
我有时使用"goto",我发现它看起来不错,就像上面的例子;
bool AskRetry(Exception ex)
{
return MessageBox.Show(you know here...) == DialogResult.Retry;
}
void SomeFuncOrEventInGui()
{
re:try{ SomeThing(); }
catch (Exception ex)
{
if (AskRetry(ex)) goto re;
else Mange(ex); // or throw or log.., whatever...
}
}
Run Code Online (Sandbox Code Playgroud)
我知道你可以递归地做同样的事情,但谁关心它只是工作而我使用.
要添加到此处的其他答案,除了打破嵌套循环之外,goto 的一种巧妙用法是简单而干净的状态机:
goto EntryState;
EntryState:
//do stuff
if (condition) goto State1;
if (otherCondition) goto State2;
goto EntryState;
State1:
//do stuff
if (condition) goto State2;
goto State1;
State2:
//do stuff
if (condition) goto State1;
goto State2;
Run Code Online (Sandbox Code Playgroud)
这是一种非常干净且可读的状态机方式,而且最重要的是它的性能友好并且免费!虽然您可以在不使用 goto 的情况下执行此操作,但它实际上只会降低代码的可读性。不要回避 goto,只是在使用它之前想一下。
我同意大多数关于goto有多糟糕的答案.
我避免goto的消化是这样的:
while (condition) { // Main Loop
for (int i = 0; i < 15; i++) { // Secondary loop
// Do Something
if(someOtherCondition){
condition = false // stops the main loop
break; // breaks inner loop
}
}
}
Run Code Online (Sandbox Code Playgroud)