开发者

GoTo statements and alternatives in VB.NET

I've posted a code snippet on another forum asking for help and people pointed out to me that using GoTo statements is very bad programming practice. I'm wondering: why is it bad?

What alternatives to GoTo are there to use in VB.NET that would be considered generally more of开发者_JAVA技巧 a better practice?

Consider this snippet below where the user has to input their date of birth. If the month/date/year are invalid or unrealistic, I'd like to loop back and ask the user again. (I'm using if statements to check the integer's size... if there's a better way to do this, I'd appreciate if you could tell me that also :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


I'm going to differ from everyone else and say that GOTOs themselves are not all the evil. The evil comes from the misuse of GOTO.

In general, there is almost always better solutions than using a GOTO, but there really are times when GOTO is the proper way to do it.

That being said, you are a beginner, so you shouldn't be allowed to judge if GOTO is proper or not (because it hardly ever is) for a few more years.

I would write your code like this (my VB is a bit rusty...):

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

If you take your GOTO code and look at it, how would someone first approach your code? "Hmm.. retryday? What does this do? When does this happen? Oh, so we goto that label if the day is out of range. Ok, so we want to loop until the date is considered to be valid and in range".

Whereas if you look at mine:

"Oh, we want to keep doing this until it's Valid. It is valid when the date is within range."


http://xkcd.com/292/ I think this is the standard opinion of GoTo.

Instead, try and use a Do Until loop. Do Until loops will always execute once and are great when you need to prompt the user and want to make sure that you do not proceed until they enter the correct information.

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


The GOTO construct produces sphagetti code. This makes tracing through code almost impossible.

Procedural / Functional programming is a much better approach.


Questions about the merits of the GoTo statement (or rather the lack thereof) are perennial on this site. Click here for an example: Is GoTo still considered harmful?

With regards to an alternative to GoTo, in the provided snippet, a while loop would nicely do the trick, maybe something like:

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


GOTOs are a pretty political issue. The 'solution' to GOTOs is to use other built-in navigation constructs like functions, methods, loops, etc. For VB, you could make a sub-procedure that runs that code, or put it in a While loop. You can google both of those subjects fairly easily.


A little clunky but:

    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

Also consider some basic loops in "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

Here's the nice equivalent:

   For x As Integer = 0 To 10
        Debug.WriteLine(i)
    Next
    For x As Integer = 0 To 10
        Debug.WriteLine(i * 2)
    Next

Which is more readable and less error prone?


Using goto has been considered a bad practice for decades now. Perhaps it was a backlash against the original BASIC (before Visual Basic). In the original BASIC there were no while loops, no local variables (only globals), and (in most BASIC versions) functions could not take parameters or return values. Moreover, functions were not explicitly separated; control can implicitly fell from one function to another if you forgot a RETURN statement. Finally, code indentation was a foreign concept in these early BASICs.

If you used the original BASIC for a some time (like I did), you would come to appreciate how the use of global variables and gotos everywhere makes a large program hard to understand, and without great care, turned it into a tangled mess of "spaghetti". When I learned QBASIC, with its WHILE..WEND loops and SUBs, I never looked back.

I don't think gotos hurt in small quantities, but in the coder culture a strong sense lingers that they are somehow evil. Therefore, I would avoid gotos for no other reason than to avoid offending sensibilities. Occasionally I find that a goto solves a problem cleanly (like breaking out of an outer loop from within an inner loop), but you should consider whether another solution makes the code more readable (e.g. put the outer loop in a separate function and use "exit function", instead of goto, in the inner loop).

I wrote a C++ program with perhaps 100,000 lines of code and I've used goto 30 times. Meanwhile, there are more than 1,000 "normal" loops and around 10,000 "if" statements.


Functions FTW!

Okay, I'm not sure if your code is really VB.Net here, since you've got some wonky type stuff going on (i.e. Console.Readline returns a String, not a number you can do comparisons on)... so we'll just forget about type for the moment.

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

And separately

Function ValidDate(day) As Boolean
  Return day > 31 Or day < 1
End Function

Or you could have fun with recursion and early-return syntax! ;)

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


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


You can do almost anything that you can do with GOTOs with simple built-in language constructs like decision structures and loops, and GOTO statements often make for messy, impossible-to-understand spaghetti code. Loops and ifs and such have a clear, accepted, understandable usage.

See, as is usually suggested, Dijkstra's Go-To Statement Considered Harmful


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.


I'll throw mine even though the 'by the book' wolves outhere will downvote. Have a look at : Is it ever advantageous to use 'goto' in a language that supports loops and functions? If so, why?


I've got to agree with everyone else here: GOTO itself is not evil, but misusing it will certainly make your life miserable. There are so many other control structures to choose from, and a well-written program can usually handle most every situation without goto. That being said, I am at the near-completion point of a program that's racking up about 15,000 lines, and I used one, and only one, GOTO statement (which I may be replacing we will see). It's the first time I've used GOTO in the last dozen or so programs I've dealt with. But in this instance it got rid of a compiler error (using Me.Close() twice within the same Sub but within different If structures; I could have suppressed it but I simply threw in a label and replaced one Me.Close() with a GoTo CloseLabel). If I start running into more instances that require Me.Close() within this Sub, I'm likely to put Me.Close() in its own sub and simply call that sub from the If structures or other loops that result in a closing of the program... As I said, there's alternatives, but sometimes, and when used very rarely, sparingly, and strategically, GoTo can still be helpful. Just beware of spaghetti code, that's a blinking mess lol


Your code is fine. It is concise and clear. It is better than inflating the job 50% to 200% with extra variables and different verbs that do the same thing.

If you are just skipping backwards or forwards to the beginning or end of a logical block, then go(to) for it. A "Loop" or an "End while" is still a goto, but the destination is implied. The only advantage is that the compiler will stop you from making two loops cross paths, but it can't with a pair of gotos. When using goto's, don't cross the streams. That would be bad. -- Dr. Spengler

My other pet peeve is the "one entrance, one exit" rule. Of course you can only have one entrance, unless you are writing in assembler. But the "one exit" rule is stupid. It just leads to a bunch of nested bounds checks that walks your code off the right margin. It is much more clear to test all your parameters at the top of the routine and "exit sub" if they are illegal. What makes more sense?

if badparam then
  log error
  exit sub
  endif

if badparam2 then
  log error2
  exit sub
  endif

do stuff

or this?

if goodparam then
  if goodparam2 then
    do stuff
  else
    log error2
    endif
else
  log error 
  endif 

When you have six bounds checks and "stuff" is 60 lines that you can't break up into smaller bits, then the second way turns into a nightmare for anyone who has to maintain it. It's better to finish what you were doing -- checking the exceptions -- than to defer all the exception handling to the end.

My $0.02

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜