goto in action - The exception from the rule

18/06/2022
C#goto

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:

  1. In almost any case where you use a goto it tends to be easier when you refactor code.
  2. 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:

0
An error has occurred. This application may no longer respond until reloaded. Reload x