goto
is one of those keywords where almost everyone agrees that it is considered "bad". But there are valid uses cases for that.
The first question we have to ask ourselves is Why is it bad?.
Why is goto
bad?
There two main reasons I want to show:
- In almost any case where you use a
goto
it tends to be easier when you refactor code. goto
lets your control flow jump around
Let's assume you have the following code:
for (var i = 0; i < n; i++)
{
for (var j = 0; j < m; j++)
{
if (condition)
goto EndOfLoop;
}
}
EndOfLoop:
Console.Write("Do something here");
Here goto
seems a viable choice, doesn't it? Consider the alternative with break
. break
allows only to break out one level:
var breakOut = false;
for (var i = 0; i < n; i++)
{
for (var j = 0; j < m; j++)
{
if (condition)
{
breakOut = true;
break;
}
}
if (breakOut)
break;
}
Not that beautiful. So one could say that is a valid use case. But as said earlier those situtations tend to have better solutions where we refactor the whole loop into its own function an return directly a value bypassing any use of goto
or break
in the first place.
The valid use case
Of course it boils down to personal preference and also the stuff shown below can be considered less maintainable or readable. Anyway I want to present: goto case
. Oversimplified have a look at this:
enum PathKind
{
Relative = 0,
Absolute,
}
private void SaveFile(string relativeOrAbsolutePath, PathKind pathKind)
{
switch(pathKind)
{
case PathKind.Relative:
relativeOrAbsolutePath = Path.Combine(currentPath, relativeOrAbsolutePath);
SaveFile(relativeOrAbsolutePath);
break;
case PathKind.Absolute:
SaveFile(relativeOrAbsolutePath);
break;
}
We see that in both cases we want to save a file with the absolute path but we can't share that logic. Now why not using C style with fall through control flow like this:
private void SaveFile(string relativeOrAbsolutePath, PathKind pathKind)
{
switch(pathKind)
{
case PathKind.Relative:
relativeOrAbsolutePath = Path.Combine(currentPath, relativeOrAbsolutePath);
SaveFile(relativeOrAbsolutePath);
case PathKind.Absolute:
SaveFile(relativeOrAbsolutePath);
break;
}
Easy answer: Because C# does not allow this.
Meet goto case
:
private void SaveFile(string relativeOrAbsolutePath, PathKind pathKind)
{
switch(pathKind)
{
case PathKind.Relative:
relativeOrAbsolutePath = Path.Combine(currentPath, relativeOrAbsolutePath);
goto case PathKind.Absolute;
case PathKind.Absolute:
SaveFile(relativeOrAbsolutePath);
break;
}
Resources:
goto
in the Microsoft documentation