转到被视为有害的声明?
如果上面的说法是正确的,那么为什么当我在 .Net BCL 上使用反射器时,我看到它被大量使用?
编辑:让我重新表述一下:我在反射器中看到的所有 GO-TO 都是由人类或编译器编写的吗?
If the statement above is correct, then why when I use reflector on .Net BCL I see it is used a lot?
EDIT: let me rephrase: are all the GO-TO's I see in reflector written by humans or compilers?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
我认为以下关于 Goto 的维基百科文章的摘录在这里特别相关:
因此,一方面,我们有Edsger Dijkstra(一位非常有才华的计算机科学家)争论< em>反对使用
GOTO
语句,特别反对过度使用GOTO
语句,理由是:这是一种不太结构化的代码编写方式。另一方面,我们有 Donald Knuth(另一位非常有才华的计算机科学家)认为使用
GOTO
,特别是明智地使用它实际上可以成为给定的程序代码片段的“最佳”和最优化的构造。最终,恕我直言,我相信这两个人都是正确的。 Dijkstra 的观点是正确的,过度使用
GOTO
语句肯定会降低一段代码的可读性和结构性,从纯理论角度看待计算机编程时确实如此。然而,Knuth 也是正确的,因为在“现实世界”中,人们必须采取务实的方法,明智地使用 GOTO 语句确实可以成为最佳选择要使用的语言构造。
I think the following excerpt from the Wikipedia Article on Goto is particularly relevant here:
So, on the one hand we have Edsger Dijkstra (a incredibly talented computer scientist) arguing against the use of the
GOTO
statement, and specifically arguing against the excessive use of theGOTO
statement on the grounds that it is a much less structured way of writing code.On the other hand, we have Donald Knuth (another incredibly talented computer scientist) arguing that using
GOTO
, especially using it judiciously can actually be the "best" and most optimal construct for a given piece of program code.Ultimately, IMHO, I believe both men are correct. Dijkstra is correct in that overuse of the
GOTO
statement certainly makes a piece of code less readable and less structured, and this is certainly true when viewing computer programming from a purely theoretical perspective.However, Knuth is also correct as, in the "real world", where one must take a pragmatic approach, the
GOTO
statement when used wisely can indeed be the best choice of language construct to use.上面的内容并不完全正确 - 它是 Dijkstra 在 gotos 时使用的一种争论手段是唯一正在使用的流量控制结构。事实上,已经有几个人提出了反驳,包括 Knuth 的经典论文《StructuredProgrammingUsingGoto》(标题凭记忆)。在某些情况下(错误处理、状态机),goto 可以生成比“结构化”替代方案更清晰的代码(恕我直言)。
The above isn't really correct - it was a polemical device used by Dijkstra at a time when gotos were about the only flow control structure in use. In fact, several people have produced rebuttals, including Knuth's classic "Structured Programming Using Goto" paper (title from memory). And there are some situations (error handling, state machines) where gotos can produce clearer code (IMHO), than the "structured" alternatives.
这些 goto 通常是由编译器生成的,尤其是在枚举器内部。
编译器总是知道她在做什么。
如果您发现自己需要使用
goto
,您应该确保它是唯一的选择。大多数情况下,您会发现有更好的解决方案。除此之外,在极少数情况下使用
goto
是合理的,例如使用嵌套循环时。同样,在这种情况下还有其他选择。您可以打破函数中的内部循环并使用 return 语句代替。您需要仔细查看额外的方法调用是否真的成本太高。响应您的编辑:
不,并非所有 goto 都是编译器生成的,但其中很多是由编译器生成的状态机(枚举器)、switch case 语句或优化的 if else 结构产生的。只有少数情况下您才能判断是编译器还是原始开发人员造成的。您可以通过查看函数/类名称来获得良好的提示,编译器将生成“禁止”名称以避免与您的代码发生名称冲突。如果一切看起来正常并且代码尚未优化或混淆,则可能打算使用 goto。
These
goto
's are very often generated by the compiler, especially inside enumerators.The compiler always knows what she's doing.
If you find yourself in the need to use
goto
, you should make sure it is the only option. Most often you'll find there's a better solution.Other than that, there are very few instances the use of
goto
can be justified, such as when using nested loops. Again, there are other options in this case still. You could break out the inner loop in a function and use a return statement instead. You need to look closely if the additional method call is really too costly.In response to your edit:
No, not all gotos are compiler generated, but a lot of them result from compiler generated state machines (enumerators), switch case statements or optimized if else structures. There are only a few instances you'll be able to judge whether it was the compiler or the original developer. You can get a good hint by looking at the function/class name, a compiler will generate "forbidden" names to avoid name clashes with your code. If everything looks normal and the code has not been optimized or obfuscated the use of goto is probably intended.
请记住,您在 Reflector 中看到的代码是反汇编代码——Reflector 正在查看编译后的字节代码并尝试将原始源代码拼凑在一起。
因此,您必须记住,针对
goto
的规则适用于高级代码。用于替换goto
的所有结构(for
、while
、break
、switch< /code> 等)都使用 JMP 编译为代码。
因此,Reflector 像这样看待代码:
并且必须意识到它实际上被编码为:
有时读取的代码太复杂而无法识别模式。
Keep in mind that the code you are seeing in Reflector is a disassembly -- Reflector is looking at the compiled byte codes and trying to piece together the original source code.
With that, you must remember that rules against
goto
s apply to high-level code. All the constructs that are used to replacegoto
s (for
,while
,break
,switch
etc) all compile down to code using JMPs.So, Reflector looks at code much like this:
And must realize that it was actually coded as:
Sometimes the code being read to too complicated for it to recognize the pattern.
Go To
语句本身并无害处,有时甚至非常有用。有害的是那些倾向于将其放在代码中不适当位置的用户。Go To
statement itself is not harmful, it is even pretty useful sometimes. Harmful are users who tend to put it in inappropriate places in their code.当编译为汇编代码时,所有控制都会结构化并转换为(非)条件跳转。然而,优化器可能太强大了,当反汇编器无法识别跳转模式对应的控制结构时,就会发出始终正确的语句,即
goto label;
。这与
goto
的危害性无关。When compiled down to assembly code, all control structured and converted to (un)conditional jumps. However, the optimizer may be too powerful, and when the disassembler cannot identify what control structure a jump pattern corresponds to, the always-correct statement, i.e.
goto label;
will be emitted.This has nothing to do with the harm(ful|less)ness of
goto
.例如,双循环或许多嵌套循环(您已突破其中的循环)怎么样?
在这种情况下,您应该考虑使用中断来完成以下操作:
what about a double loop or many nested loops, of which you have break out, for ex.
in this case you should consider that here the following should be done with break:
一般规则是您不需要使用
goto
。与任何规则一样,当然也有例外,但与任何例外一样,例外情况很少。goto
命令就像毒品。如果仅在特殊情况下限量使用,那就很好。如果你一直使用太多,它会毁掉你的生活。当您使用 Reflector 查看代码时,您看不到实际的代码。您看到的代码是根据编译器从原始代码生成的代码重新创建的。当您在重新创建的代码中看到
goto
时,并不确定原始代码中是否存在goto
。可能有一个更结构化的命令来控制流程,例如break
或continue
,编译器以与goto< 相同的方式实现/code>,这样 Reflector 就无法区分。
The general rule is that you don't need to use
goto
. As with any rule there are of course exceptions, but as with any exceptions they are few.The
goto
command is like a drug. If it's used in limited amounts only in special situations, it's good. If you use too much all the time, it will ruin your life.When you are looing at the code using Reflector, you are not seeing the actual code. You are seeing code that is recreated from what the compiler produced from the original code. When you see a
goto
in the recreated code, it's not certain that there was agoto
in the original code. There might be a more structured command to control the flow, like abreak
or acontinue
which has been implemented by the compiler in the same way as agoto
, so that Reflector can't tell the difference.因为无论我们(人类)多么疯狂地使用
goto
,编译器总是知道如何阅读代码。相信我...
使用
goto
阅读其他代码其中的 code> 很难。阅读自己的带有
goto
的代码更加困难。这就是为什么你看到它用于低级(机器语言)而不是高级(人类语言,例如 C#、Python...);)
because no matter how madly we(human) use
goto
, compiler always knows how to read the code.Believe me...
Reading others code with
goto
s in it is HARD.Reading your own code with
goto
s in it is HARDER.That is why you see it used in low level (machine languages) and not in high level (human languages e.g. C#,Python...) ;)
“C 提供了无限滥用的 goto 语句和分支标签。从形式上来说,goto 从来都不是必需的,而在实践中,没有它几乎总是很容易编写代码。我们还没有使用 goto在这本书里。”
-- K&R(第二版):第 65 页
"C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto is never necessary, and in practice it is almost always easy to write code without it. We have not used goto in this book."
-- K&R (2nd Ed.) : Page 65
当我想执行终止操作时,我有时会使用 goto:
因此,我不是按
return
,而是将其发送到执行另一个最终操作的最后一行,我可以从代码片段中的各个位置引用,而不是刚刚终止。I sometimes use goto when I want to perform a termination action:
So instead of hitting
return
, I send it to the last line that performs another final action I can refer to from various places in the snippet instead of just terminating.在反编译的代码中,几乎所有您看到的 goto 都是合成的。不用担心他们;它们是如何在低级别表示代码的产物。
至于将它们放入您自己的代码中的正当理由吗?我能想到的主要问题是您使用的语言没有提供适合您正在解决的问题的控制结构;可以轻松创建自定义控制流系统的语言通常根本没有
goto
。完全避免使用它们也总是可能的,但是将任意复杂的代码重新排列到一个 while 循环和大量带有一整套控制变量的条件中......这实际上会使代码变得更加晦涩难懂(而且速度也较慢;编译器通常不够聪明,无法区分这种复杂性)。编程的主要目标应该是生成对计算机和阅读程序的人来说都清晰的程序描述。In decompiled code, virtually all
goto
s that you see will be synthetic. Don't worry about them; they're an artifact of how the code is represented at the low level.As to valid reasons for putting them in your own code? The main one I can think of is where the language you are using does not provide a control construct suitable for the problem you are tackling; languages which make it easy to make custom control flow systems typically don't have
goto
at all. It's also always possible to avoid using them at all, but rearranging arbitrarily complex code into a while loop and lots of conditionals with a whole battery of control variables... that can actually make the code even more obscure (and slower too; compilers usually aren't smart enough to pick apart such complexity). The main goal of programming should be to produce a description of a program that is both clear to the computer and to the people reading it.有害与否,就看各人的喜好了。我个人不喜欢它们,并且发现它们在尝试代码的可维护性时效率很低。
现在,一件事是 goto 如何影响我们对代码的阅读,而另一件事是找到 goto 时抖动如何执行。来自 Eric Lippert 的博客,我想引用一下:
因此,实际上编译器在发出 IL 时将几乎每个流控制结构转换为 goto/label 模式。当反射器读取程序集的 IL 时,它会识别该模式,并将其转换回适当的流控制结构。
在某些情况下,当发出的代码对于反射器来说太复杂而无法理解时,它只会向您显示使用标签和 goto 的 C# 代码,相当于它正在读取的 IL。例如,使用
yield return
和yield break
语句实现IEnumerable
方法时就是这种情况。这些类型的方法使用底层状态机转换为它们自己的实现IEnumerable
接口的类。我相信在BCL你会发现很多这样的案例。If it's harmful or not, it's a matter of likes and dislikes of each one. I personally don't like them, and find them very unproductive as they attempt maintainability of the code.
Now, one thing is how gotos affect our reading of the code, while another is how the jitter performs when found one. From Eric Lippert's Blog, I'd like to quote:
So, in fact the compiler transforms pretty each flow control structure into goto/label pattern while emitting IL. When reflector reads the IL of the assembly, it recognizes the pattern, and transforms it back to the appropriate flow control structure.
In some cases, when the emitted code is too complicated for reflector to understand, it just shows you the C# code that uses labels and gotos, equivalent to the IL it's reading. This is the case for example when implementing
IEnumerable<T>
methods withyield return
andyield break
statements. Those kind of methods get transformed into their own classes implementing theIEnumerable<T>
interface using an underlying state machine. I believe in BCL you'll find lots of this cases.如果没有如上所述过度使用,GOTO 可能会很有用。 Microsoft 甚至在 .NET Framework 本身的多个实例中使用它。
GOTO can be useful, if it's not overused as stated above. Microsoft even uses it in several instances within the .NET Framework itself.
这些 goto 通常由编译器生成,尤其是在枚举器内部。编译器总是知道她在做什么。
如果您发现自己需要使用 goto,您应该确保它是唯一的选择。大多数情况下,您会发现有更好的解决方案。
除此之外,很少有情况可以证明使用 goto 是合理的,例如使用嵌套循环时。同样,在这种情况下还有其他选择。您可以打破函数中的内部循环并使用 return 语句代替。您需要仔细查看额外的方法调用是否真的成本太高。
These goto's are very often generated by the compiler, especially inside enumerators. The compiler always knows what she's doing.
If you find yourself in the need to use goto, you should make sure it is the only option. Most often you'll find there's a better solution.
Other than that, there are very few instances the use of goto can be justified, such as when using nested loops. Again, there are other options in this case still. You could break out the inner loop in a function and use a return statement instead. You need to look closely if the additional method call is really too costly.