This article discusses some common usage scenarios of the goto keyword and shows alternative approaches.
 |
|
comic by xkcd.com ^
|
If someone would have asked me a few years ago when to use the goto keyword I would have answered (in capital letters): NEVER!! GOTO’S ARE EVIL! (and obviously you get attacked by raptors if you use them)
Like many I have been educated to have a emotional reaction when someone mentions the goto keyword. Emotions have nothing to do with good practices, so lets take a pragmatic look on some of the common usages of goto and see what the real value of the goto keyword is.
Using goto to provide a single exit point.
The author of Sams Teach Yourself C# in 24 Hours (published by O’Reilly) describes the usage of gotos like this: (code slightly modified to make it more readable)
[...]This discussion may leave you wondering why you would ever use goto. One situation in which I commonly use goto’s is to create single exit points. As you know, you can force execution to leave a method at any time using return. Often, clean-up code is required before a method exits. In a long method, you may have many return statements. However, such a method can be a problem to debug because clean-up code may not be run under all circumstances. Because all methods have a single entry point, it makes sense to give them a single exit point. With a single exit point, you use a goto statement to go to the exit point, rather than use a return statement. The following procedure illustrates using goto to create a single exit point:
private void btnGoto_Click(object sender, System.EventArgs e)
{
//...
// If it is necessary to exit the code, perform a goto to
// the PROC_EXIT label, rather than using an Exit statement.
if (someValue)
goto PROC_EXIT;
//...
PROC_EXIT:
//...
return;
}
The author suggests using gotos for cleanup code and to provide a single exit point. This seems logical and it might be the most common use of gotos, it is however in C# absolutely unnecessary. (I guess if someone wants to learn C# in 24 hours there is no time to learn it the ‘right’ way.) To force cleanup code we can and should use a simple try - finally pattern (read more about it in my article).
We can rewrite the code above to this
private void btnGoto_Click(object sender, System.EventArgs e)
{
//...
try
{
//... (can even contain return statements or throw Exceptions)
}
finally
{
//cleanup here
}
}
This approach has two major advantages:
-
you don’t have to remember to use goto instead of return
-
the cleanup code is executed even if there is an exception thrown
Using goto to jump between switch statements.
Coming up with a reasonable example for a goto in a switch statement has proven to be difficult, which in itself might be an indicator for its usefulness. Anyway, basically you can jump from one case in a switch code block to another using the goto statement.
switch (something)
{
case 1:
//some code
goto case 2;
case 2:
//some code
if (someExpression)
goto case 1;
//some more code
goto case 4;
case 3:
//some code
break;
case 4:
//some code
goto default;
default:
break;
}
Let’s just take a look at this code snippet to discuss some of the upsides and downsides of goto statements in a switch code block. One good thing with labels in the switch statements (case 1, case 2 ect.) is that they are only accessible within the scope of the switch statement, which means that you cannot jump into the switch statement. This makes it easier to read but as you can see in the example above it makes it more complicated to follow the execution since it is no longer linear. Especially if the switch block is longer than a page and you use conditional goto’s (such as the one in the if (someExpression) block) it can be a real challenge to follow the execution flow. Unfortunately there is nothing to prevent you from defining a loop in which case the program will cause a StackOverFlowException. In the example above the goto case 1 in case 2 sends it back to case 2 and it could potentially be send back to case 1 again creating a loop. I would suggest to be open for the usage of the goto statement in switch blocks unlesst he complexity is high enough to make it painful to debug or there are possibilities for loops.
Using goto to break out of nested loops.
Another popular usage of gotos is breaking from nested loops. Unfortunately there is no break all keyword that would help us to break out of nested loops so we have to find another way of doing this.
for (int i = 0; i < 25; i++)
{
for (int j = 0; j < 25; j++)
{
if (j == 4)
goto exitLoop;
//...
}
}
exitLoop:
//cleanup
return;
As long as the exitLoop label is immediately after the loop there is little chance that anything goes wrong here thus I think it is quite okay to use goto this way but you can also use a bool variable to achieve the same.
bool breakAll;
for (int i = 0; i < 25; i++)
{
for (int j = 0; j < 25; j++)
{
if (j == 4)
{
breakAll = true;
break;
}
//...
}
if (breakAll)
break;
}
The later has the disadvantage that you have to remember to check for the breakAll variable in every loop. A better option in my opinion is to refactor the nested loops into a single method where you can use the return statement in order to break out of them.
So, when do you use goto’s?
I didn’t have to use a single goto statement in production code myself but I can see some situations where it might be efficient to use one. I guess the reason why I am not using it is because most of the time there is a better or equal alternative and I am one of these developers who enjoys refactoring/improving code which means that if I have to chose between ‘refactor the program flow’ or ‘use this one little goto statement’ it is highly likely that I will do the former. (and that has nothing to do with my fear of raptors)