10

我在另一个论坛上发布了一个代码片段寻求帮助,人们向我指出使用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
4

14 回答 14

19

我要与其他人不同,并说 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?这是做什么的?什么时候发生?哦,所以如果日期超出范围,我们会转到那个标签。好的,所以我们想要循环直到日期被认为是有效的并且在范围内” .

而如果你看看我的:

“哦,我们想一直这样做,直到它有效。日期在范围内时有效。”

于 2010-04-23T17:27:47.647 回答
6

http://xkcd.com/292/我认为这是GoTo.

相反,请尝试使用Do Until循环。Do Until循环将始终执行一次,当您需要提示用户并希望确保在他们输入正确信息之前不继续执行时,循环将非常有用。

Sub Main()
    'Every time the loop runs, this variable will tell whether
    'the user has finally entered a proper value.
    Dim Valid As Boolean = False

    'This is the variable which stores the final number which user enters.
    Dim Day As Integer = 0
    Do Until Valid
        Console.WriteLine("Enter the day:")
        Dim DayStr As String = Console.ReadLine()

        If Not Integer.TryParse(DayStr, Day) Then
            Console.WriteLine("Invalid value! It must be a valid number.")
            Valid = False
        ElseIf (Day < 1) Or (Day > 31) Then
            onsole.WriteLine("Invalid day! It must be from 1 to 31.")
           Valid = False
        Else
           Valid = True
        End If
    Loop

    'blablabla
    'Do whatever you want, with the Day variable
End Sub
于 2010-04-23T17:20:51.487 回答
2

GOTO构造产生 sphagetti 代码。这使得通过代码进行跟踪几乎是不可能的。

过程/函数式编程是一种更好的方法。

于 2010-04-23T17:18:20.260 回答
2

关于该GoTo声明的优点(或更确切地说是缺乏)的问题在本网站上常年存在。单击此处查看示例: GoTo 仍然被认为是有害的吗?

关于 的替代方案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
于 2010-04-23T17:25:34.240 回答
1

功能FTW!

好的,我不确定你的代码是否真的是 VB.Net,因为你有一些不稳定的类型的东西(即Console.Readline返回 a 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
于 2012-09-25T18:13:47.407 回答
1

几十年来,使用 goto 一直被认为是一种不好的做法。也许这是对原始 BASIC(在 Visual Basic 之前)的强烈反对。在最初的 BASIC 中没有 while 循环,没有局部变量(只有全局变量),并且(在大多数 BASIC 版本中)函数不能接受参数或返回值。此外,功能没有明确分离;如果您忘记了 RETURN 语句,控制可能会隐式地从一个函数转移到另一个函数。最后,代码缩进在这些早期的 BASIC 中是一个陌生的概念。

如果你使用原始的 BASIC 一段时间(就像我一样),你会明白全局变量和 goto 的使用是如何让一个大程序难以理解的,并且不加注意,把它变成了一团乱七​​八糟的“意大利面”。当我学习 QBASIC 及其 WHILE..WEND 循环和 SUB 时,我从未回头。

我不认为 goto 的伤害很小,但在编码器文化中,一种强烈的感觉仍然存在,即它们在某种程度上是邪恶的。因此,除了避免冒犯敏感性外,我会避免使用 goto。有时我发现 goto 可以干净利落地解决问题(比如从内循环中跳出外循环),但您应该考虑另一种解决方案是否使代码更具可读性(例如,将外循环放在单独的函数中并使用 "退出函数”,而不是 goto,在内部循环中)。

我用大约 100,000 行代码编写了一个 C++ 程序,我已经使用了 30 次 goto。同时,有超过 1,000 个“正常”循环和大约 10,000 个“if”语句。

于 2010-04-23T21:07:49.540 回答
1

GOTO 是一个非常政治化的问题。GOTO 的“解决方案”是使用其他内置导航结构,如函数、方法、循环等。对于 VB,您可以创建一个运行该代码的子过程,或将其放在 While 循环中。你可以很容易地用谷歌搜索这两个主题。

于 2010-04-23T17:18:31.597 回答
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

哪个更具可读性且不易出错?

于 2010-04-23T17:30:29.563 回答
0

It is often recommended that we follow Dijkstra's advice in Go-To Statement Considered Harmful.

Donald Knuth answered Dijkstra pretty soundly. This example here is a modern version of one of his counterexamples. Personally I write infinite loops with internal breaks when I encounter this one but there's a few other rare cases where I will write GOTO statements.

The most common ones for me are breaking out of deeply nested loops and this pattern:

ContinueTry:
   Try
        'Worker code
   Catch ex as IO.IOException
        If MessageBox.Show(...) = DialogResult.Retry Then Goto ContinueTry
        Throw
   End Try

I also have two cases of large finite state machines with goto statements providing the transitions.

于 2010-04-23T17:42:32.313 回答
0

即使外面的“书本”狼会投反对票,我也会扔掉我的。看看:在支持循环和函数的语言中使用“goto”是否有利?如果是这样,为什么?

于 2014-10-16T13:58:32.583 回答
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
于 2010-04-23T17:17:39.857 回答
0

你几乎可以GOTO用简单的内置语言结构(如决策结构和循环)来做任何你可以用 s 做的事情,而GOTO语句通常会导致混乱、无法理解的意大利面条代码。循环和 if 等具有清晰、可接受、可理解的用法。

请参阅,正如通常建议的那样,Dijkstra 的Go-To 声明被认为是有害的

于 2010-04-23T17:19:14.310 回答
0

我必须同意这里的其他人的观点:GOTO 本身并不邪恶,但滥用它肯定会让你的生活变得悲惨。还有很多其他的控制结构可供选择,一个编写良好的程序通常可以处理大多数情况而无需 goto。话虽如此,我正在接近完成大约 15,000 行的程序,并且我使用了一个,而且只有一个 GOTO 语句(我可能会替换我们将看到的)。这是我在最近处理的十几个程序中第一次使用 GOTO。但在这种情况下,它消除了编译器错误(在同一个 Sub 但在不同的 If 结构中使用 Me.Close() 两次;我本可以抑制它,但我只是输入一个标签并将一个 Me.Close() 替换为转到关闭标签)。如果我开始遇到更多需要我的实例。Close() 在这个 Sub 中,我很可能将 Me.Close() 放在它自己的 sub 中,并简单地从 If 结构或其他导致程序关闭的循环中调用该 sub ...正如我所说,有替代方案,但有时,并且在极少、谨慎和战略性地使用时,GoTo 仍然可以提供帮助。小心意大利面条代码,这是一个闪烁的混乱哈哈

于 2016-05-09T13:03:38.600 回答
-2

你的代码很好。它简洁明了。这比用额外的变量和做同样事情的不同动词将工作膨胀 50% 到 200% 更好。

如果您只是向后或向前跳到逻辑块的开头或结尾,那么 go(to) 就可以了。“Loop”或“End while”仍然是 goto,但目的地是隐含的。唯一的好处是编译器会阻止你让两个循环交叉路径,但它不能用一对 goto。使用 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 美元

于 2010-04-23T20:17:20.427 回答