goto in action - The exception from the rule

6/18/2022
3 minute read

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:

NDepend

NDepend is a static analysis tool for .NET managed code. The tool proposes a large number features, from dependency visualization to Quality Gates and Smart Technical Debt Estimation. For that reasons the community refers to it as the "Swiss Army Knife" for .NET Developers.

So let's check if that descriptionI shamelessly stole from Wikipedia checks out and what we can do with that tool.

Throwing exceptions - Why is my stack trace lost?

You might have read, that re-throwing an exception like this: throw exc; is considered bad practice and you should just do this: throw; instead.

But why is it like that?

Leverage 'is not' Operator for Exception Filtering!

Did you know you can use the 'is not' operator with exception filtering to simplify and improve your error handling in C#?

In this short blog post, I will show you how to use it.

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