'转到'这是不好的?
在做了一些关于如何突破二级循环的研究之后
while (true) { // Main Loop for (int I = 0; I < 15; I++) { // Secondary loop // Do Something break; // Break main loop? } }
大多数人build议调用“goto”function
看下面的例子:
while (true) { // Main Loop for (int I = 0; I < 15; I++) { // Secondary Loop // Do Something goto ContinueOn; // Breaks the main loop } } ContinueOn:
然而; 我经常听说“goto”声明是不好的做法。 下面的图片完全说明了我的观点:
所以
- goto声明有多糟糕,为什么?
- 有没有比使用“goto”声明更有效的方式来打破主循环?
编辑:
goto声明有多糟糕,为什么?
这取决于确切的情况。 我不记得任何时候我发现它使得代码比重构更可读。 这也取决于你对可读性的个人看法 – 有些人比其他人更不喜欢它,从其他答案中可以明显看出。 (作为一个兴趣点,它被广泛用于生成代码 – C#5中的所有asynchronous/等待代码都基于有效的很多gotos)。
问题在于,往往使用goto
情况往往是那种重构辅助工具的情况,而goto
是随着代码变得越来越复杂而越来越难以遵循的解决scheme。
有没有比使用“goto”声明更有效的方式来打破主循环?
绝对。 将你的方法提取到一个单独的函数中:
while (ProcessValues(...)) { // Body left deliberately empty } ... private bool ProcessValues() { for (int i = 0; i < 15; i++) { // Do something return false; } return true; }
我通常宁愿这样做,通过引入一个额外的局部variables来跟踪“我已经完成” – 尽pipe这当然会起作用。
goto声明有多糟糕,为什么?
这是所有正常的理由真的很糟糕。 在不支持它们的语言中模拟带标签的循环时效果很好。
在很多情况下用函数代替它将分散逻辑确实应该被看作是相同的单元。 这使得它更难以阅读。 没有人喜欢追踪一些function,这些function在旅程结束之前什么也不做,什么时候你已经有点忘记了从哪里开始。
用布尔variables和一堆额外的ifs和break来代替它实际上是笨重的,并且使它更难以遵循真正的意图,就像任何噪声一样。
在Java (和JavaScript),这是完全可以接受的(标签为循环):
outer: while( true ) { for( int i = 0; i < 15; ++i ) { break outer; } }
在C#中,它看起来像是非常接近的等价物:
while( true ) { for (int I = 0; I < 15; I++) { goto outer; } } outer:;
由于goto
这个词,它具有使人们放弃所有常识的心理作用,并使得它们与上下文无关地联系起来。
有没有比使用“goto”声明更有效的方式来打破主循环?
在某些情况下,没有,这就是为什么其他语言提供标签循环,C#提供了goto
。 请注意,您的示例太简单了,它使得解决方法看起来不会太糟糕,因为它们是针对该示例进行的裁剪。 其实我也可以这样build议:
for (int I = 0; I < 15; I++) { break; }
这个怎么样:
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:;
这对你还是很好看:
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; }
我会强烈反对在这里的所有其他答案。 您使用goto
提供的代码没有任何问题。 有一个原因 C#有一个goto
语句,这正是你描述的这些types的场景。
goto
只是有一个负面的耻辱,因为在二十世纪七十年代和以前的人会写出可怕的,完全不可维护的代码,由于goto
控制stream量跳过所有的地方。 C#的goto
甚至不允许在方法之间转换! 然而,这种不合理的耻辱反对它。
在我看来,使用“现代” goto
到内部循环绝对没有错。 人们所提供的“替代品”总是最终变得更加复杂和难以阅读 。
方法通常被认为是可重用的 。 为一个循环的内部部分创build一个完整的单独的方法,只能从那个位置调用,而且方法实现可能最终位于源代码中的某个较远位置,这不是一个改进。
这种傻瓜式的运动,以避免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 } } }
我有时使用“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... } }
我知道你可以recursion地做同样的事情,但谁在乎它只是工作,我使用。
我的一个同事(他有15年的固件编程经验)和我一直使用goto。 但是,我们只用它来处理exception! 例如:
if (setsockopt(sd,...)<0){ goto setsockopt_failed; } ... setsockopt_failed: close(sd);
据我所知,这是在C中处理exception的最好方法。我相信,我也为C#工作。 另外,我不是唯一这样认为的人: C或C ++中好的gotos的例子
就我个人而言,我喜欢把goto当成“goto lead to hell”……在很多方面这是真的,因为它会导致高度不可维护的代码和非常糟糕的做法。
这就是说,它仍然是有原因的,如果没有其他解决scheme的话,在使用时应该谨慎使用。 面向对象的语言本身并不需要它(很多)。
我今天看到的唯一一次使用就是模糊不清……使用goto来创build地狱代码的一个很好的例子,所以人们会被阻止去理解它!
有些语言依赖于相同的关键字。 例如,在x86中,你有JMP(Jump),JE(Jump if Equal)和JZ(如果是Zero)这样的关键字。 这些在汇编语言中是经常需要的,因为在这个级别没有OO,在应用程序中移动的方法很less。
AFAIK …远离它,除非绝对必要。
当你在多个循环中使用它时,想跳出循环,你会发现它非常有用:关于这个:
foreach (var item in ResultList) { //do sth for(int i=0;i<count;i++) { //do other things,if you want break loop to the end go to finish; } } finsih: //do other