标签和GOTO
在C#中被认为是不良行为,据我所知,在C#中没有使用它的理由。
那么在C#中标签有什么用处?
标签和GOTO
在C#中被认为是不良行为,据我所知,在C#中没有使用它的理由。
那么在C#中标签有什么用处?
标签和goto本身并没有问题。问题在于人们倾向于滥用它们,这会引起问题。
典型的标签使用方式
OperationStart:
if ( !TrySomeOperation() ) {
if ( MaybeFixOperation() ) {
goto OperationStart;
}
}
你需要做出一些断言来确保不会陷入无限循环,但在合理的保证下,这段代码本质上没有任何问题。
虽然它们是不受好评的做法,但这并不意味着应该关闭使用它们的任何可能性。尽管它们可能从来不会真正被“需要”,但有时它们是最佳选择。
代码生成器有时会使用标签和goto语句。在某些情况下,这可以简化代码生成逻辑。出于可读性的原因,通常不建议使用goto语句,但是如果代码不打算被阅读,则这一点无关紧要。
我还看到过这样的情况:反编译器由于中间语言(IL)而感到困惑,输出了goto语句,而不是人类精心设计的指令序列。如果goto语句是非法的,那么反编译器可能必须完全放弃该代码部分。这样,至少它可以生成一些可以往返转换的内容。
为什么要用goto和label?
原因其实很简单。汇编语言和IL不支持像for
、do
、break
、continue
和while
这样的高级指令。隐式地,循环代码(或者更一般地说:分支代码)会被编译成分支。goto
和
请注意,即使有通常更好的替代方案(在这一点上,我想指出有像状态模式
这样的设计模式),也不能轻易地实现我所能想到的所有构造,而没有使用'goto'。
如果你设计一个类似C#的语言,它是IL的“高级汇编语言”,那么至少支持IL中的所有内容是有意义的。在这里,分支/标签是低级结构,goto/label是C#的投影(加上作用域作为高级结构)。
从编译器的角度来看
最终,如果您深入了解编译器的工作原理,会发现有可预测的代码和不可预测的代码之分。编译器中优化部分花费了大量精力将您代码中使用的结构规范化。一旦规范化,就会使用模式进行优化。所以虽然所有编译器都将循环转换为特定形式的分支,但IL编译器是一种SSA编译器,就像LLVM一样。这个Chandler的视频解释了一些关于它如何工作的知识: https://www.youtube.com/watch?v=FnGCDLhaxKU
没有使用 goto
的标签是无用的,它们什么也不做。
使用 goto
被认为是一种不好的实践。但有一种情况是无法避免的:跳出嵌套循环:
foreach(...) {
foreach(...) {
if(...) {
goto OuterLabel;
}
}
}
OuterLabel:
break
语句只会中断最内层的循环。我认为这是一项营销决策。
微软希望各种类型的开发人员都使用他们的C#语言,如果添加标签,可以使某些程序员更容易地进行转换。它还可以更轻松地将旧代码移植到他们的语言中...
我在某处读到,大多数情况下,goto应该只用于向前跳转,因此很可能只用于早期循环终止和继续外部循环,因为C#中没有标记的循环(与Java不同)。而且有一些算法可以通过goto来优雅地表达,而不是按结构化方式执行。
没有它们,做 switch 语句很困难。