C# Goto

Goto keyword

Statements execute one after another. With goto, though, we transfer control to a named label elsewhere in the method. Goto can be used to exit a deeply nested loop. As are many things, it is considered harmful.

Example

Program

To start, we examine a program that uses goto to exit a nested loop. We can use code that does the same thing without goto. But this would require a flag variable (usually a bool) to check. Method syntax is another option.

BoolMethods

Label:With a label, we indicate where a goto-statement should transfer control.

Based on:

.NET 4.5

C# program that uses goto

using System;

class Program
{
    static void Main()
    {
	Console.WriteLine(M());
    }

    static int M()
    {
	int dummy = 0;
	for (int a = 0; a < 10; a++)
	{
	    for (int y = 0; y < 10; y++) // Run until condition.
	    {
		for (int x = 0; x < 10; x++) // Run until condition.
		{
		    if (x == 5 &&
			y == 5)
		    {
			goto Outer;
		    }
		}
		dummy++;
	    }
	Outer:
	    continue;
	}
	return dummy;
    }
}

Output

50
Squares

In this example, method M contains three nested loops. The first loop iterates through numbers [0, 9], as do the two inner loops. But in the third loop, a condition is checked that causes the loop to exit using the break keyword.

ForBreak

The code increments the dummy variable after each completion of the inner loop. If the inner loop is exited early, this variable should be left alone. With the goto statement, it is not incremented.

Result:The value 50 is printed to the console. The int is incremented 10 x 5 times.

However:If the goto was a break, the result would be 10 x 10 times, or a total of 100.

Example 2

Performance

Continuing on, we show how to rewrite code that uses the break and continue keywords to use the goto and continue keywords. You may think the goto code is clearer. It uses one fewer variable.

Note:Our benchmark is of limited utility. It does show how the JIT may be affected by the goto statement.

Code that uses break, for-loop: C#

static int BreakMethod()
{
    int dummy = 0;
    for (int a = 0; a < 10; a++)
    {
	for (int y = 0; y < 10; y++) // Run until condition.
	{
	    bool ok = true;
	    for (int x = 0; x < 10; x++) // Run until condition.
	    {
		if (x == 5 &&
		    y == 5)
		{
		    ok = false;
		    break;
		}
	    }
	    if (!ok)
	    {
		break;
	    }
	    dummy++;
	}
	continue;
    }
    return dummy;
}

Code that uses goto on loop: C#

static int GotoMethod()
{
    int dummy = 0;
    for (int a = 0; a < 10; a++)
    {
	for (int y = 0; y < 10; y++) // Run until condition.
	{
	    for (int x = 0; x < 10; x++) // Run until condition.
	    {
		if (x == 5 &&
		    y == 5)
		{
		    goto Outer;
		}
	    }
	    dummy++;
	}
    Outer:
	continue;
    }
    return dummy;
}
Bool keyword

This code uses a flag variable named "ok". It initializes the variable to true, but then sets it to false and breaks when the exit condition is reached. This avoids incrementing the dummy variable. The result is 50.

True, False

Next:Our goto example has the same effect. It detects the exit condition and uses goto to leave the inner loop. It has no flag variable.

Benchmark

Just-in-time compiler: JIT

I placed the above two methods in a benchmark program. I ran them in a loop for many iterations. The function result was checked each time. I tested them with the break first and the goto second, and the other way around.

So:The results were that goto performed worse when it was run first. Timings are shown next.

Benchmark
Performance results

break:   1033 ms   (goto tested first)
goto:    1156 ms

break:   1036 ms   (break tested first)
goto:    1029 ms
Copyright

The behavior observed here shows that goto is sometimes not optimized as well with the JIT compiler. Goto is only slower when called in the first loop. This might mean that the JIT compiler does not work as well with it.

Therefore:With these methods, break is faster because it is more reliably JIT-optimized.

JIT

Switch

Switch

The goto statement also can be used in a switch block. The switch allows control to be transferred to another case in this way. Goto still transfers the flow of control, but causes somewhat less confusion in switch.

Tip:To look on the bright side, this can enhance performance and reduce code size in some cases.

Syntax:There is a special syntax for this construct: it requires goto case or goto default. We examine this syntax form.

Switch
C# program that uses goto statement in switch

using System;

class Program
{
    static void Main()
    {
	Console.WriteLine(GetPrice(1000));
	Console.WriteLine(GetPrice(-1));
	Console.WriteLine(GetPrice(int.Parse("100")));
    }

    static int GetPrice(int id)
    {
	//
	// Default price is 5.
	//
	int price = 5;
	//
	// When id is 1000, add 10 to price before multiplying.
	//
	switch (id)
	{
	    case 1000:
		price += 10; // <-- Could get from another method
		goto case 100;
	    case 100:
		return price * 10;
	    default:
		return price;
	}
	//
	// 1.
	// First, if ID is 1000, add ten to default price.
	//
	// 2.
	// If ID is 1000 or 100, multiply price by 10 and return it.
	//
	// 3.
	// If ID is anything else, return price of 5.
	//
    }
}

Output

150
5
50
Method

In this example, we see a custom method called GetPrice defined. We often find switches in single methods like this one. GetPrice() performs three logical steps, all defined in the switch-statement.

Note:The method receives the ID of an item. If the ID is equal to 1000, the base price is increased by 10.

ID attribute

And:After this occurs, this ID is treated like a price of 100. It goes to that case.

Case

Next steps. For IDs of 1000 or 100, the value returned from the method is equal to the base price times 10. This occurs after IDs of 1000 were processed. For all other values, the price that is returned is the base price, which is 5.

Benefits, switch

Cover logo

Quality code is structured into discrete methods. We can simply call the same method with different values from a switch. But in certain cases we may benefit from goto in switch when trying to reduce method size.

Method Size Test

Info:The greatest benefit of switches on ints or non-string constants here is that the low-level MSIL jump statement is used.

IntSwitch Enumswitch InstructionIL

Research

Future

Goto was part of the 1960s emergence of structural programming. In his famous text "Go To Statement Considered Harmful," Dijkstra observed how goto-statements make logic more complex, harder to follow.

And:New options, like methods, are clearer. With them, programs are less likely to deteriorate into unfathomable messes.

Spaghetti CodeExclamation mark

The go to statement as it stands is just too primitive, it is too much an invitation to make a mess of one's program.

Edsger W. Dijkstra: utexas.edu

Formally, the goto is never necessary, and in practice it is almost always easy to write code without it.... It does seem that goto statements should be used rarely, if at all.

The C Programming Language

Summary

Goto may look fast and convenient. But it can confuse the runtime and result in poorer performance. Thus goto is not only more complex for programmers. It is more difficult for the JIT compiler to optimize.


C#: Loop