转到声明有害吗?

如果上面的陈述是正确的,那么为什么当我在.Net BCL上使用reflection器时,我看到它被大量使用了?

编辑:让我重新说一下:我在人类或编译器编写的reflection器中看到的所有GO-TO是什么?

以上情况并不十分正确 – 这是Dijkstra在使用时唯一的流量控制结构时使用的一种化学装置。 事实上,有几个人已经产生了反驳,包括Knuth经典的“使用Goto进行结构化编程”论文(来自记忆的标题)。 并且有一些情况(error handling,状态机),其中gotos可以产生比“结构化”替代品更清晰的代码(恕我直言)。

我认为以下关于Goto的维基百科文章的摘录在这里特别相关:

可能最着名的批评GOTO的是1968年由Edsger Dijkstra写的一封名为Go To Statement Considered Harmful的信。 在那封信中,Dijkstra认为不应该从更高级别的语言中废除不受限制的GOTO语句,因为它们使分析和validation程序正确性(特别是涉及循环的程序)的任务变得复杂。 Donald Knuth的结构化编程中提供了另一种观点,即转到语句,该语句分析了许多常见的编程任务,并发现其中一些GOTO是使用的最佳语言结构。

因此,一方面我们有Edsger Dijkstra (一位非常有才华的计算机科学家) 反对使用GOTO声明,并特别反对过度使用GOTO声明,理由是它是一种结构化程度低得多的写作方式码。

另一方面,我们有Donald Knuth (另一位非常有才华的计算机科学家)认为使用GOTO ,特别是明智地使用GOTO实际上可以成为给定程序代码的“最佳”和最优构造。

最终,恕我直言,我相信这两个人都是正确的。 Dijkstra是正确的, 过度使用 GOTO语句肯定会使一段代码的可读性和结构性降低,从纯理论角度看计算机编程时,这当然是正确的。

然而,Knuth也是正确的,因为在“现实世界”中,必须采用务实的方法,明智地使用GOTO语句确实可以成为语言构造的最佳选择。

这些goto通常由编译器生成,尤其是在枚举器中。 编译器总是知道她在做什么。

如果你发现自己需要使用goto ,你应该确保它是唯一的选择。 大多数情况下,你会发现有更好的解决方案。

除此之外,很少有实例可以certificate使用goto ,例如使用嵌套循环时。 同样,在这种情况下还有其他选择。 您可以在函数中分解内部循环并使用return语句。 您需要仔细查看附加方法调用是否真的太昂贵。


为了回应您的编辑:

不,并非所有的getos都是编译器生成的,但很多都是由编译器生成的状态机(枚举器),switch case语句或优化的if else结构产生的。 只有少数几个实例可以判断它是编译器还是原始开发人员。 您可以通过查看函数/类名来获得良好的提示,编译器将生成“禁用”名称以避免与您的代码发生名称冲突。 如果一切看起来正常并且代码未经过优化或混淆,则可能会使用goto。

请记住,您在Reflector中看到的代码是反汇编 – Reflector正在查看已编译的字节代码并尝试拼凑原始源代码。

有了这个,您必须记住针对goto的规则适用于高级代码。 所有用于替换gotoforwhilebreakswitch等)的构造都使用JMP编译成代码。

因此,Reflector看起来像这样的代码:

 A: if !(a > b) goto B; DoStuff(); goto A; B: ... 

并且必须意识到它实际编码为:

  while (a > b) DoStuff(); 

有时,读取的代码太复杂,无法识别模式。

Go To语句本身无害,有时甚至非常有用。 有害的用户倾向于将其放在代码中的不适当位置。

当编译成汇编代码时,所有控件都被构造并转换为(非)条件跳转。 但是,优化器可能过于强大,并且当反汇编程序无法识别跳转模式对应的控制结构时,总是正确的语句,即goto label; 将被发出。

这与goto的伤害(​​没有)无关。

怎么样的双循环或许多嵌套循环,你已经爆发了,例如。

  foreach (KeyValuePair changedValForDate in _changedValForDates) { foreach (KeyValuePair TypVal in changedValForDate.Value.TypeVales) { RefreshProgress("Daten werden geändert...", count++, false); if (IsProgressCanceled) { goto TheEnd; //I like goto :) } } } TheEnd: 

在这种情况下,您应该考虑以下应该使用break来完成:

  foreach(KeyValuePair changedValForDate in _changedValForDates) { foreach (KeyValuePair TypVal in changedValForDate.Value.TypeVales) { RefreshProgress("Daten werden geändert...", count++, false); if (IsProgressCanceled) { break; //I miss goto :| } } if (IsProgressCanceled) { break; //I really miss goto now :| }//waaaAA !! so many brakets, I hate'm } 

一般规则是您不需要使用goto 。 与任何规则一样,当然有例外情况,但与任何例外情况一样,它们很少。

goto命令就像一种毒品。 如果仅在特殊情况下使用数量有限,那就很好。 如果你一直使用太多,它将毁了你的生活。

当您使用Reflector使用代码时,您没有看到实际的代码。 您将看到从编译器从原始代码生成的内容重新创建的代码。 当您在重新创建的代码中看到goto时,不确定原始代码中是否有goto 。 可能有一个更结构化的命令来控制流,比如一个break或者一个由编译器以与goto相同的方式实现的continue ,这样Reflector就无法区分它们。

goto被认为是有害的(供人使用,但对计算机来说还可以)。

因为无论我们(人类)使用goto多么疯狂, 编译器总是知道如何读取代码。

相信我…

goto s读取其他代码是很难的。
goto s读取你自己的代码是很难的。

这就是为什么你看到它用在低级别(机器语言)而不是高级别(人类语言,例如C#,Python ……);)

“C提供了无限可重用的goto语句,并且标签分支到。正式来说,goto 从来都不是必需的,实际上编写没有它的代码几乎总是很容易。我们在本书中没有使用goto。”

K&R (第2版):第65页

当我想执行终止操作时,我有时会使用goto:

 static void DoAction(params int[] args) { foreach (int arg in args) { Console.WriteLine(arg); if (arg == 93) goto exit; } //edit: if (args.Length > 3) goto exit; //Do another gazillion actions you might wanna skip. //etc. //etc. exit: Console.Write("Delete resource or whatever"); } 

所以我没有点击return ,而是将其发送到最后一行,执行另一个最终操作,我可以从代码段中的各个位置引用而不是仅仅终止。

在反编译代码中,您看到的几乎所有goto都是合成的。 别担心; 它们是代码如何在低级别表示的工件。

至于将它们放入您自己的代码中的正当理由? 我能想到的主要问题是你所使用的语言不能提供适合你正在处理的问题的控制结构; 使自定义控制流系统变得容易的语言通常根本没有goto 。 它总是可以避免使用它们,但是将任意复杂的代码重新排列成一个while循环和许多带有一大堆控制变量的条件……这实际上可以使代码更加模糊(也更慢;编译器通常是不够聪明,挑选出这样的复杂性)。 编程的主要目标应该是生成对计算机和阅读计算机的人都清楚的程序的描述。

如果它有害或无益,那就是每个人的喜好和不喜欢的问题。 我个人不喜欢它们,并且发现它们非常无效,因为它们试图保持代码的可维护性。

现在,有一件事是如何影响我们对代码的读取,而另一件事是抖动如何在找到时执行。 来自Eric Lippert的博客 ,我想引用:

我们首先运行一个pass来将循环转换为gotos和labels。

因此,实际上编译器将每个流控制结构转换为goto / label模式,同时发出IL。 当reflection器读取组件的IL时,它识别该模式,并将其转换回适当的流控制结构。

在某些情况下,当发出的代码太复杂而无法理解reflection器时,它只会显示使用标签和gotos的C#代码,相当于它读取的IL。 例如,在使用yield returnyield break语句实现IEnumerable方法时就是这种情况。 这些方法被转换为他们自己的类,使用底层状态机实现IEnumerable接口。 我相信BCL你会发现很多这种情况。

如果它没有如上所述过度使用,GOTO可能很有用。 Microsoft甚至在.NET Framework本身的多个实例中使用它。

这些goto通常由编译器生成,尤其是在枚举器中。 编译器总是知道她在做什么。

如果你发现自己需要使用goto,你应该确保它是唯一的选择。 大多数情况下,你会发现有更好的解决方案。

除此之外,很少有实例可以certificate使用goto,例如使用嵌套循环时。 同样,在这种情况下还有其他选择。 您可以在函数中分解内部循环并使用return语句。 您需要仔细查看附加方法调用是否真的太昂贵。