我在另一个论坛上发布了一个代码片段,寻求帮助,人们向我指出使用GoTo
语句是非常糟糕的编程习惯。我想知道:为什么不好?
在VB.NET中有哪些替代GoTo
可以被认为是更好的做法?
请考虑以下用户必须输入其出生日期的代码段。如果月/日/年无效或不切实际,我想循环回来并再次询问用户。 (我正在使用if语句来检查整数的大小......如果有更好的方法可以做到这一点,我很感激你能告诉我那个:D)
retryday:
Console.WriteLine("Please enter the day you were born : ")
day = Console.ReadLine
If day > 31 Or day < 1 Then
Console.WriteLine("Please enter a valid day")
GoTo retryday
End If
答案 0 :(得分:18)
我会与其他人不同,并说GOTO本身并非都是邪恶的。邪恶来自对GOTO的滥用。
一般来说,几乎总是比使用GOTO更好的解决方案,但有时GOTO是正确的方法。
话虽如此,你是初学者,所以你不应该被允许判断GOTO是否适当(因为它几乎没有)再过几年。
我会像这样编写你的代码(我的VB有点生锈......):
Dim valid As Boolean = False
While Not valid
Console.WriteLine("Please enter the day you were born: ")
Dim day As String
day = Console.ReadLine
If day > 31 Or day < 1 Then
Console.WriteLine("Please enter a valid day.")
Else
valid = True
End If
End While
如果您使用GOTO代码并查看它,有人会如何处理您的代码? “嗯... retryday?这是做什么的?这是什么时候发生的?哦,所以如果当天超出范围我们就转到那个标签。好的,所以我们想循环直到日期被认为是有效的并且在范围内”
然而,如果你看看我的:
“哦,我们希望继续这样做,直到它有效。它在日期在范围内时有效。”
答案 1 :(得分:6)
http://xkcd.com/292/ 我认为这是Goto的标准意见。
而是尝试使用do while循环。 while while循环将始终执行一次,并且当您需要支持用户时很好,但请确保它们输入正确的信息。
Sub Main()
Dim valid as Integer
valid=0
Do Until valid = 1
System.Console.WriteLine("enter the day ")
day = System.Console.ReadLine()
If day > 31 Or day < 1 Then
System.Console.WriteLine("Invalid day\n")
valid = 0;
Else
valid = 1
Loop
End Sub
答案 2 :(得分:2)
GOTO
构造生成sphagetti代码。这使得跟踪代码几乎不可能。
程序/功能编程是一种更好的方法。
答案 3 :(得分:2)
关于GoTo
声明的优点的问题(或者说缺乏声明)在本网站上是常年的。点击此处查看示例:Is GoTo still considered harmful?
关于GoTo
的替代方法,在提供的代码段中,while
循环可以很好地完成这一操作,可能类似于:
day = -1
While (day < 0)
Console.WriteLine("Please enter the day you were born : ")
day = Console.ReadLine
If day > 31 Or day < 1 Then
Console.WriteLine("Please enter a valid day")
day = -1
End If
End While
答案 4 :(得分:1)
GOTO是一个非常政治问题。 GOTO的“解决方案”是使用其他内置的导航结构,如函数,方法,循环等。对于VB,您可以创建一个运行该代码的子过程,或者将它放在While循环中。你可以很容易地谷歌这两个主题。
答案 5 :(得分:1)
有点笨重但是:
Dim bContinue As Boolean
Console.WriteLine("Enter a number between 1 and 31")
Do
Dim number As Integer = Console.ReadLine()
If number >= 1 AndAlso number <= 31 Then
bContinue = True
Else
Console.WriteLine("Please enter a VALID number between 1 and 31")
End If
Loop Until bContinue
还要考虑“goto land”中的一些基本循环
Dim i As Integer
startofloop1:
Debug.WriteLine(i)
i += 1
If i <= 10 Then
GoTo startofloop1
End If
i = 0
startofloop2:
Debug.WriteLine(i * 2)
i += 1
If i <= 10 Then
GoTo startofloop2
End If
这是很好的等价物:
For x As Integer = 0 To 10
Debug.WriteLine(i)
Next
For x As Integer = 0 To 10
Debug.WriteLine(i * 2)
Next
哪个更具可读性且不易出错?
答案 6 :(得分:1)
几十年来,使用goto被认为是一种不好的做法。也许这是对原始BASIC(在Visual Basic之前)的反对。在原始的BASIC中没有while循环,没有局部变量(只有全局变量),并且(在大多数BASIC版本中)函数不能获取参数或返回值。而且,功能没有明确分开;如果忘记了RETURN语句,控制可以隐式地从一个函数掉落到另一个函数。最后,代码缩进在这些早期的BASIC中是一个外国概念。
如果你使用原版BASIC已经有一段时间了(就像我一样),你就会明白如何使用全局变量和各种各样的函数使大型程序难以理解,并且没有经过精心处理,将其转化为混乱的“意大利面”。当我学习QBASIC时,我的WHILE..WEND循环和SUBs,我从未回头。
我不认为少量受到伤害,但在编码文化中,一种强烈的感觉依赖于它们在某种程度上是邪恶的。因此,除了避免冒犯情感之外,我会避免其他原因。偶尔我发现goto干净地解决了一个问题(比如从内部循环中突破外部循环),但你应该考虑另一个解决方案是否使代码更具可读性(例如将外部循环放在一个单独的函数中并使用“退出函数“,而不是goto,在内循环中。”
我写了一个C ++程序,大概有10万行代码,我已经使用了goto 30次。同时,有超过1,000个“正常”循环和大约10,000个“if”语句。
答案 7 :(得分:1)
功能FTW!
好的,我不确定你的代码是否真的是VB.Net,因为你有一些不稳定的类型的东西(即Console.Readline
返回String
,而不是你的数字可以做比较)...所以我们暂时忘记了类型。
Console.Writeline("Please enter the day you were born : ")
day = Console.Readline()
While not ValidDate(day)
Console.WriteLine("Please enter a valid day")
day = Console.Readline()
End While
并单独
Function ValidDate(day) As Boolean
Return day > 31 Or day < 1
End Function
或者你可以通过递归和早期返回语法获得乐趣! ;)
Function GetDate() As String
Console.Writeline("Please enter the day you were born : ")
day = Console.Readline()
If ValidDate(day) Then Return day 'Early return
Console.Writeline("Invalid date... try again")
GetDate()
End Function
答案 8 :(得分:0)
While True
Console.WriteLine("Please enter the day you were born : ")
day = Console.ReadLine
If day > 31 Or day < 1 Then
Console.WriteLine("Please enter a valid day")
Continue
Else
Break
End If
End While
答案 9 :(得分:0)
您可以使用简单的内置语言结构(如决策结构和循环)对GOTO
进行任何操作,并且GOTO
语句通常会导致混乱,不可理解的意大利面条码。循环和ifs等具有清晰,可接受,可理解的用法。
按照通常的建议,参见Dijkstra的Go-To Statement Considered Harmful
答案 10 :(得分:0)
通常建议我们遵循Dijkstra在Go-To Statement中认为有害的建议。
唐纳德克努特非常合理地回答了迪克斯特拉。这个例子是他的一个反例的现代版本。当我遇到这个时,我个人写了内部中断的无限循环但是还有一些其他罕见的情况我将编写GOTO语句。对我来说最常见的是突破深嵌套循环和这种模式:
ContinueTry:
Try
'Worker code
Catch ex as IO.IOException
If MessageBox.Show(...) = DialogResult.Retry Then Goto ContinueTry
Throw
End Try
我还有两个大型有限状态机的情况,其中goto语句提供了转换。
答案 11 :(得分:0)
即使“书中的狼群”无处可见,我也会扔掉它。 看看:Is it ever advantageous to use 'goto' in a language that supports loops and functions? If so, why?
答案 12 :(得分:0)
我必须同意其他所有人:GOTO本身并不邪恶,但滥用它肯定会让你的生活变得悲惨。还有很多其他控制结构可供选择,编写良好的程序通常可以处理大多数情况而无需goto。话虽这么说,我正处于一个程序的接近完成点,该计划正在筹集大约15,000行,并且我使用了一个,只有一个,GOTO声明(我可能会替换我们将看到)。这是我第一次在我处理过的十几个项目中使用过GOTO。但是在这种情况下,它摆脱了编译器错误(在同一个Sub中使用Me.Close()两次但在不同的If结构中;我可以抑制它但是我只是扔了一个标签并用一个Me.Close()替换了一个GoTo CloseLabel)。如果我开始遇到需要此Sub中的Me.Close()的更多实例,我可能会将Me.Close()放在它自己的sub中,并简单地从If结构或其他循环中调用该子,从而导致关闭该计划...正如我所说,有其他选择,但有时,当很少使用,谨慎使用,并且战略性地,GoTo仍然可以提供帮助。请注意意大利面条代码,这是一个眨眼的混乱lol
答案 13 :(得分:-2)
你的代码很好。它简洁明了。使用额外的变量和不同的动词做同样的事情,比将工作膨胀50%到200%更好。
如果您只是向后或向前跳到逻辑块的开头或结尾,那么转到(去)它。 “循环”或“结束时”仍然是一个转到,但目的地是隐含的。唯一的好处是编译器将阻止你制作两个循环交叉路径,但它不能与一对gotos。使用goto时,请勿跨越流。那会很糟糕。 - 斯宾格勒博士
我的另一个宠儿小便是“一个入口,一个出口”规则。当然,你只能有一个入口,除非你是用汇编语言写的。但“一个退出”规则是愚蠢的。它只是导致一堆嵌套的边界检查,使你的代码偏离正确的边缘。在例程的顶部测试所有参数并且如果它们是非法的则“退出子”则更加清楚。什么更有意义?
if badparam then
log error
exit sub
endif
if badparam2 then
log error2
exit sub
endif
do stuff
还是这个?
if goodparam then
if goodparam2 then
do stuff
else
log error2
endif
else
log error
endif
当你有六个边界检查并且“东西”是60行,你不能分成更小的位,那么第二种方式变成了任何必须维护它的人的噩梦。最好完成你正在做的事情 - 检查异常 - 而不是将所有异常处理推迟到最后。
我的$ 0.02