Jump Statements

It is possible to alter the execution path of a loop. In fact, with jump statements, it is possible to escape out of the loop or to skip the remaining portion of an iteration and begin with the next iteration, even when the loop condition remains true. This section considers some of the ways to jump the execution path from one location to another.

The break Statement

To escape out of a loop or a switch statement, C# uses a break statement. Whenever the break statement is encountered, control immediately leaves the loop or switch. Listing 4.51 examines the foreach loop from the tic-tac-toe program, and Output 4.24 shows the output.

Listing 4.51: Using break to Escape Once a Winner Is Found
int winner = 0;
// Stores locations each player has moved
int[] playerPositions = { 0, 0 };
 
// Hardcoded board position
//  X | 2 | O 
// ---+---+---
//  O | O | 6 
// ---+---+---
//  X | X | X 
playerPositions[0] = 449;
playerPositions[1] = 28;
 
// Determine if there is a winner
int[] winningMasks = {
    7, 56, 448, 73, 146, 292, 84, 273 };
 
// Iterate through each winning mask to determine
// if there is a winner
foreach (int mask in winningMasks)
{
    if ((mask & playerPositions[0]) == mask)
    {
        winner = 1;
        break;
    }
    else if((mask & playerPositions[1]) == mask)
    {
        winner = 2;
        break;
    }
}
 
Console.WriteLine(
    $"Player { winner } was the winner");
Output 4.24
Player 1 was the winner

Listing 4.51 uses a break statement when a player holds a winning position. The break statement forces its enclosing loop (or a switch statement) to cease execution, and control moves to the next line outside the loop. For this listing, if the bit comparison returns true (if the board holds a winning position), the break statement causes control to jump and display the winner.

Beginner Topic
Bitwise Operators for Positions

The full tic-tac-toe listing uses the bitwise operators to determine which player wins the game. First, the code saves the positions of each player into a bitmap called playerPositions. (It uses an array with two items so that the positions for both players can be saved.)

To begin, both playerPositions are 0. As each player moves, the bit corresponding to the move is set. If, for example, the player selects cell 3, shifter is set to 3 – 1. The code subtracts 1 because C# is zero based and you need to adjust for 0 as the first position instead of 1. Next, the code sets position, the bit corresponding to cell 3, using the shift operator 000000000000001 << shifter, where shifter now has a value of 2. Lastly, it sets playerPositions for the current player (subtracting 1 again to shift to zero based) to 0000000000000100. Listing 4.52 uses |= so that previous moves are combined with the current move.

Listing 4.52: Setting the Bit That Corresponds to Each Player’s Move
int shifter;  // The number of places to shift 
              // over to set a bit
int position; // The bit that is to be set
 
// int.Parse() converts "input" to an integer.
// "int.Parse(input) - 1" because arrays 
// are zero based.
shifter = int.Parse(input) - 1;
 
// Shift mask of 00000000000000000000000000000001
// over by cellLocations.
position = 1 << shifter;
 
// Take the current player cells and OR them to set the
// new position as well.
// Since currentPlayer is either 1 or 2,
// subtract 1 to use currentPlayer as an
// index in a zero based array.
playerPositions[currentPlayer - 1] |= position;

Later in the program, you can iterate over each mask corresponding to winning positions on the board to determine whether the current player has a winning position, as shown in Listing 4.51.

The continue Statement

You might have a block containing a series of statements within a loop. If you determine that some conditions warrant executing only a portion of these statements for some iterations, you can use the continue statement to jump to the end of the current iteration and begin the next iteration. The continue statement exits the current iteration (regardless of whether additional statements remain) and jumps to the loop condition. At that point, if the loop conditional is still true, the loop continues execution.

Listing 4.53 uses the continue statement so that only the letters of the domain portion of an email are displayed. Output 4.25 shows the results of Listing 4.53.

Listing 4.53: Determining the Domain of an Email Address
string email;
bool insideDomain = false;
 
Console.Write("Enter an email address: ");
email = Console.ReadLine() ?? string.Empty;
 
Console.Write("The email domain is: ");
 
// Iterate through each letter in the email address
foreach(char letter in email)
{
    if(!insideDomain)
    {
        if(letter == '@')
        {
            insideDomain = true;
        }
        continue;
    }
 
    Console.Write(letter);
}
Output 4.25
Enter an email address: mark@IntelliTect.com
The email domain is: IntelliTect.com

In Listing 4.53, if you are not yet inside the domain portion of the email address, you can use a continue statement to move control to the end of the loop and process the next character in the email address.

You can almost always use an if statement in place of a continue statement, and this format is usually more readable. The problem with the continue statement is that it provides multiple flows of control within a single iteration, which compromises readability. In Listing 4.54, the example in Listing 4.53 has been rewritten by replacing the continue statement with the if/else construct to demonstrate a more readable version that does not use the continue statement.

Listing 4.54: Replacing a continue Statement with an if Statement
// Iterate through each letter in the email address
foreach (char letter in email)
{
    if(insideDomain)
    {
        Console.Write(letter);
    }
    else
    {
        if(letter == '@')
        {
            insideDomain = true;
        }
    }
}
The goto Statement

Early programming languages lacked the sophisticated “structured” control flows that modern languages such as C# have as a matter of course. Instead, they relied on simple conditional branching (if) and unconditional branching (goto) statements for most of their control flow needs. The resultant programs were often hard to understand. The continued existence of a goto statement within C# seems like an anachronism to many experienced programmers. However, C# supports goto, and it is the only method for supporting fall-through within a switch statement. In Listing 4.55, if the /out option is set, code execution jumps to the default case using the goto statement, and similarly for /f. Output 4.26 provides an example of the program executing.

Listing 4.55: Demonstrating a switch with goto Statements
// ...
public static void Main(string[] args)
{
    bool isOutputSet = false;
    bool isFiltered = false;
    bool isRecursive = false;
 
    foreach(string option in args)
    {
        switch(option)
        {
            case "/out":
                isOutputSet = true;
                isFiltered = false;
                // ...
                goto default;
            case "/f":
                isFiltered = true;
                isRecursive = false;
                // ...
                goto default;
            default:
                if(isRecursive)
                {
                    // Recurse down the hierarchy
                    Console.WriteLine("Recursing...");
                    // ...
                }
                else if(isFiltered)
                {
                    // Add option to list of filters
                    Console.WriteLine("Filtering...");
                    // ...
                }
                break;
        }
    }
 
    // ...
}
Output 4.26
C:\SAMPLES>Generate /r /f "*.xml,*.wsdl"

To branch to a switch section label other than the default label, you can use the syntax goto case constant;, where constant is the constant associated with the case label you wish to branch to. To branch to a statement that is not associated with a switch section, precede the target statement with any identifier followed by a colon; you can then use that identifier with the goto statement. For example, you could have a labeled statement myLabel : Console.WriteLine();. The statement goto myLabel; would then branch to the labeled statement. Fortunately, C# prevents you from using goto to branch outside of the goto statements scope; instead, goto may be used only to branch within the immediate code block or to an enclosing code block. By enforcing these restrictions, C# avoids most of the serious goto abuses possible in other languages.

In spite of the improvements, use of goto is generally considered to be inelegant, difficult to understand, and symptomatic of poorly structured code. If you need to execute a section of code multiple times or under different circumstances, either use a loop or extract code to a method of its own.

Guidelines
AVOID using goto.
{{ snackbarMessage }}
;