Acceptable uses for the goto statement in C#

Introduction

There has been a lot of hysteria about the 'goto' statement since the famous computer scientist, Edsger Dijkstra, said in 1968 that he considered it to be "harmful".

In those days, he had a point because the 'goto' statement produced a lot of spaghetti code particularly by those using early versions of the BASIC programming language.

Despite this warning, 'goto' was included in C, C++ and C#. It wasn't included in Java though they do have labelled 'break' and 'continue' statements which achieve much the same thing.

In C#, it's much tamer than in some other languages as you can't jump into a nested block; you can only jump out of (or within) one. As far as the compiler is concerned, it works quickly because it can be mapped directly to CIL, and thence to native code, 'jump' instructions.

Although 'goto' isn't needed much in modern programming and it's always possible to program around it, I believe its use is acceptable in certain scenarios which I'd like to examine in this article. The scenarios presented are not necessarily exhaustive.

Jumping between 'case' blocks in a 'switch' statement

Unlike C, C++ and Java, C# doesn't permit code to 'fall through' from one case block to another unless the first case block is empty. This eliminates some potentially hard to find bugs. If you do need to fall through, you can use the 'goto case' construct instead:

string position = "first"
switch (position)
{
    case "first":
        DoSomething();
        goto case "second"; // not allowed to 'fall through' here
    case "second":
        DoSomethingElse();
        break;
}

This is nice and clean and I don't think many C# developers would quarrel with this usage.

Jumping out of (or within) deeply nested loops

The 'break' statement only jumps out of the current loop. If you want to exit from two or more nested loops, then the usual solution is to use bool variables in the following manner:

bool finished = false
for (int i = 0; i < 10; i++)
{
    while (someCondition)
    {
        //  ....
        if (someOtherCondition)
        {
            finished = true;
            break;
        }
    }
    if (finished) break;
}

Now, this is a reasonable solution when you only have two nested loops but, if you have more than this, then it can become unwieldy and error prone. In this situation, using 'goto' is much simpler and clearer:

for (int i = 0; i < 10; i++)
{
    for (int j = 0; j < 10; j++)
    {
        while (someCondition)
        {
            //  ....
            if (someOtherCondition)
            {
                goto finished;
            }
        }
    }
}
 
finished:
// code following loops

Similar considerations apply to the 'continue' keyword which can only continue the current loop. If you want to continue an outer loop, then 'goto' can be used instead of bool variables:

for (int i = 0; i < 10; i++)
{
    for (int j = 0; j < 10; j++)
    {
        while (someCondition)
        {
            //  ....
            if (someOtherCondition)
            {
                goto continuation;
            }
        }
    } 
   continuation: ; // empty statement here
}

Although having to jump to an empty statement is rather ugly, it's usually cleaner than the alternatives.

Retrying code after making corrections

The situation sometimes arises where you'd like to try a piece of code and, if it fails, make a correction and try it again. As C# lacks a 'retry' statement, you might come up with code such as this:

int x = 4, y = 0;
int z;
bool success;
 
do
{
    success = true;
 
    try
    {
        z = x / y;
    }
    catch (DivideByZeroException)
    {
        y = 2;
        success = false;
    }
 
} while (!success);
 
Console.WriteLine(z);

This works and isn't too bad but using 'goto' is cleaner and clearer:

int x = 4, y = 0;
int z;
 
retry:
try
{
    z = x / y;
}
catch (DivideByZeroException)
{
    y = 2;
    goto retry;
}
Console.WriteLine(z);

Conclusion

I should warn you now that prejudice against the 'goto' statement runs very deep and its use is often banned by some programming teams. However, I hope I've demonstrated that there are a few scenarios where its use may be beneficial so don't ignore it, if you have the choice!