This chapter discusses C#’s program control statements. There are three categories of program control statements: selection statements, which are the if and the switch; iteration statements, which consist of the for, while, do-while, and foreach loops; and jump statements, which include break, continue, goto, return, and throw. Except for throw, which is part of C#’s exception-handling mechanism and is discussed in Chapter 13, the others are examined here.
Chapter 2 introduced the if statement. It is examined in detail here. The complete form of the if statement is
if(condition) statement;
else statement;
where the targets of the if and else are single statements. The else clause is optional. The targets of both the if and else can be blocks of statements. The general form of the if using blocks of statements is
if(condition)
{
statement sequence
}
else
{
statement sequence
}
If the conditional expression is true, the target of the if will be executed; otherwise, if it exists, the target of the else will be executed. At no time will both of them be executed. The conditional expression controlling the if must produce a bool result.
Here is a simple example that uses an if and else to report if a number is positive or negative:
// Determine if a value is positive or negative.
using System;
class PosNeg {
static void Main() {
int i;
for(i=-5; i <= 5; i++) {
Console.Write("Testing " + i + ": ");
if(i < 0) Console.WriteLine("negative");
else Console.WriteLine("positive");
}
}
}
The output is shown here:
Testing -5: negative
Testing -4: negative
Testing -3: negative
Testing -2: negative
Testing -1: negative
Testing 0: positive
Testing 1: positive
Testing 2: positive
Testing 3: positive
Testing 4: positive
Testing 5: positive
In this example, if i is less than zero, then the target of the if is executed. Otherwise, the target of the else is executed. In no case are both executed.
A nested if is an if statement that is the target of another if or else. Nested ifs are very common in programming. The main thing to remember about nested ifs in C# is that an else clause always refers to the nearest if statement that is within the same block as the else and not already associated with an else. Here is an example:
if(i == 10) {
if(j < 20) a = b;
if(k > 100) c = d;
else a = c; // this else refers to if(k > 100)
}
else a = d; // this else refers to if(i == 10)
As the comments indicate, the final else is not associated with if(j < 20) because it is not in the same block (even though it is the nearest if without an else). Rather, the final else is associated with if(i == 10). The inner else refers to if(k > 100) because it is the closest if within the same block.
The following program demonstrates a nested if. In the positive/negative program shown earlier, zero is reported as positive. However, as a general rule, zero is considered signless. The following version of the program reports zero as being neither positive nor negative.
// Determine if a value is positive, negative, or zero.
using System;
class PosNegZero {
static void Main() {
int i;
for(i=-5; i <= 5; i++) {
Console.Write("Testing " + i + ": ");
if(i < 0) Console.WriteLine("negative");
else if(i == 0) Console.WriteLine("no sign");
else Console.WriteLine("positive");
}
}
}
Here is the output:
Testing -5: negative
Testing -4: negative
Testing -3: negative
Testing -2: negative
Testing -1: negative
Testing 0: no sign
Testing 1: positive
Testing 2: positive
Testing 3: positive
Testing 4: positive
Testing 5: positive
A common programming construct that is based upon the nested if is the if-else-if ladder. It looks like this:
if(condition)
statement;
else if(condition)
statement;
else if(condition)
statement;
.
.
.
else
statement;
The conditional expressions are evaluated from the top downward. As soon as a true condition is found, the statement associated with it is executed, and the rest of the ladder is bypassed. If none of the conditions is true, then the final else clause will be executed. The final else often acts as a default condition. That is, if all other conditional tests fail, then the last else clause is executed. If there is no final else and all other conditions are false, then no action will take place.
The following program demonstrates the if-else-if ladder. It finds the smallest single-digit factor (other than 1) for a given value.
// Determine smallest single-digit factor.
using System;
class Ladder {
static void Main() {
int num;
for(num = 2; num < 12; num++) {
if((num % 2) == 0)
Console.WriteLine("Smallest factor of " + num + " is 2.");
else if((num % 3) == 0)
Console.WriteLine("Smallest factor of " + num + " is 3.");
else if((num % 5) == 0)
Console.WriteLine("Smallest factor of " + num + " is 5.");
else if((num % 7) == 0)
Console.WriteLine("Smallest factor of " + num + " is 7.");
else
Console.WriteLine(num + " is not divisible by 2, 3, 5, or 7.");
}
}
}
The program produces the following output:
Smallest factor of 2 is 2.
Smallest factor of 3 is 3.
Smallest factor of 4 is 2.
Smallest factor of 5 is 5.
Smallest factor of 6 is 2.
Smallest factor of 7 is 7.
Smallest factor of 8 is 2.
Smallest factor of 9 is 3.
Smallest factor of 10 is 2.
11 is not divisible by 2, 3, 5, or 7.
As you can see, the else is executed only if none of the preceding if statements succeeds.
The second of C#’s selection statements is switch. The switch provides for a multiway branch. Thus, it enables a program to select among several alternatives. Although a series of nested if statements can perform multiway tests, for many situations the switch is a more efficient approach. It works like this: The value of an expression is successively tested against a list of constants. When a match is found, the statement sequence associated with that match is executed. The general form of the switch statement is
case constant1:
statement sequence
break;
case constant2:
statement sequence
break;
case constant3:
statement sequence
break;
.
.
.
default:
statement sequence
break;
}
The switch expression must be of an integer type, such as char, byte, short, or int, of an enumeration type, or of type string. (Enumerations and the string type are described later in this book.) Thus, floating-point expressions, for example, are not allowed. Frequently, the expression controlling the switch is simply a variable. The case constants must be of a type compatible with the expression. No two case constants in the same switch can have identical values.
The default sequence is executed if no case constant matches the expression. The default is optional; if it is not present, no action takes place if all matches fail. When a match is found, the statements associated with that case are executed until the break is encountered.
The following program demonstrates the switch:
// Demonstrate the switch.
using System;
class SwitchDemo {
static void Main() {
int i;
for(i=0; i<10; i++)
switch(i) {
case 0:
Console.WriteLine("i is zero");
break;
case 1:
Console.WriteLine("i is one");
break;
case 2:
Console.WriteLine("i is two");
break;
case 3:
Console.WriteLine("i is three");
break;
case 4:
Console.WriteLine("i is four");
break;
default:
Console.WriteLine("i is five or more");
break;
}
}
}
The output produced by this program is shown here:
i is zero
i is one
i is two
i is three
i is four
i is five or more
i is five or more
i is five or more
i is five or more
i is five or more
As you can see, each time through the loop, the statements associated with the case constant that matches i are executed. All others are bypassed. When i is five or greater, no case constants match, so the default is executed.
In the preceding example, the switch was controlled by an int variable. As explained, you can control a switch with any integer type, including char. Here is an example that uses a char expression and char case constants:
// Use a char to control the switch.
using System;
class SwitchDemo2 {
static void Main() {
char ch;
for(ch='A'; ch<= 'E'; ch++)
switch(ch) {
case 'A':
Console.WriteLine("ch is A");
break;
case 'B':
Console.WriteLine("ch is B");
break;
case 'C':
Console.WriteLine("ch is C");
break;
case 'D':
Console.WriteLine("ch is D");
break;
case 'E':
Console.WriteLine("ch is E");
break;
}
}
}
The output from this program is shown here:
ch is A
ch is B
ch is C
ch is D
ch is E
Notice that this example does not include a default case. Remember, the default is optional. When not needed, it can be left out.
In C#, it is an error for the statement sequence associated with one case to continue on into the next case. This is called the “no fall-through” rule. This is why case sequences end with a break statement. (You can avoid fall-through in other ways, such as by using the goto discussed later in this chapter, but break is by far the most commonly used approach.) When encountered within the statement sequence of a case, the break statement causes program flow to exit from the entire switch statement and resume at the next statement outside the switch. The default sequence also must not “fall through,” and it too usually ends with break.
The no fall-through rule is one point on which C# differs from C, C++, and Java. In those languages, one case may continue on (that is, fall through) into the next case. There are two reasons that C# instituted the no fall-through rule for cases: First, it allows the compiler to freely rearrange the order of the case sequences, perhaps for purposes of optimization. Such a rearrangement would not be possible if one case could flow into the next. Second, requiring each case to explicitly end prevents a programmer from accidentally allowing one case to flow into the next.
Although you cannot allow one case sequence to fall through into another, you can have two or more case labels refer to the same code sequence, as shown in this example:
// Empty cases can fall through.
using System;
class EmptyCasesCanFall {
static void Main() {
int i;
for(i=1; i < 5; i++)
switch(i) {
case 1:
case 2:
case 3: Console.WriteLine("i is 1, 2 or 3");
break;
case 4: Console.WriteLine("i is 4");
break;
}
}
}
i is 1, 2 or 3
i is 1, 2 or 3
i is 1, 2 or 3
i is 4
In this example, if i has the value 1, 2, or 3, then the first WriteLine( ) statement executes. If i is 4, then the second WriteLine( ) statement executes. The stacking of cases does not violate the no fall-through rule, because the case statements all use the same statement sequence.
Stacking case labels is a commonly employed technique when several cases share common code. This technique prevents the unnecessary duplication of code sequences.
It is possible to have a switch as part of the statement sequence of an outer switch. This is called a nested switch. The case constants of the inner and outer switch can contain common values and no conflicts will arise. For example, the following code fragment is perfectly acceptable:
switch(ch1) {
case 'A': Console.WriteLine("This A is part of outer switch.");
switch(ch2) {
case 'A':
Console.WriteLine("This A is part of inner switch");
break;
case 'B': // ...
} // end of inner switch
break;
case 'B': // ...
The for loop was introduced in Chapter 2. Here, it is examined in detail. You might be surprised at just how powerful and flexible the for loop is. Let’s begin by reviewing the basics, starting with the most traditional forms of the for.
The general form of the for loop for repeating a single statement is
for(initialization; condition; iteration) statement;
For repeating a block, the general form is
for(initialization; condition; iteration)
{
statement sequence
}
The initialization is usually an assignment statement that sets the initial value of the loop control variable, which acts as the counter that controls the loop. The condition is a Boolean expression that determines whether the loop will repeat. The iteration expression defines the amount by which the loop control variable will change each time the loop is repeated. Notice that these three major sections of the loop must be separated by semicolons. The for loop will continue to execute as long as the condition tests true. Once the condition becomes false, the loop will exit, and program execution will resume on the statement following the for.
The for loop can proceed in a positive or negative fashion, and it can change the loop control variable by any amount. For example, the following program prints the numbers 100 to –100, in decrements of 5:
// A negatively running for loop.
using System;
class DecrFor {
static void Main() {
int x;
for(x = 100; x > -100; x -= 5)
Console.WriteLine(x);
}
}
An important point about for loops is that the conditional expression is always tested at the top of the loop. This means that the code inside the loop may not be executed at all if the condition is false to begin with. Here is an example:
for(count=10; count < 5; count++)
x += count; // this statement will not execute
This loop will never execute because its control variable, count, is greater than 5 when the loop is first entered. This makes the conditional expression, count < 5, false from the outset; thus, not even one iteration of the loop will occur.
The for loop is most useful when you will be iterating a known number of times. For example, the following program uses two for loops to find the prime numbers between 2 and 20. If the number is not prime, then its largest factor is displayed.
// Determine if a number is prime. If it is not, then
// display its largest factor.
using System;
class FindPrimes {
static void Main() {
int num;
int i;
int factor;
bool isprime;
for(num = 2; num < 20; num++) {
isprime = true;
factor = 0;
// See if num is evenly divisible.
for(i=2; i <= num/2; i++) {
if((num % i) == 0) {
// num is evenly divisible. Thus, it is not prime.
isprime = false;
factor = i;
}
}
if(isprime)
Console.WriteLine(num + " is prime.");
else
Console.WriteLine("Largest factor of " + num +
" is " + factor);
}
}
}
The output from the program is shown here:
2 is prime.
3 is prime.
Largest factor of 4 is 2
5 is prime.
Largest factor of 6 is 3
7 is prime.
Largest factor of 8 is 4
Largest factor of 9 is 3
Largest factor of 10 is 5
11 is prime.
Largest factor of 12 is 6
13 is prime.
Largest factor of 14 is 7
Largest factor of 15 is 5
Largest factor of 16 is 8
17 is prime.
Largest factor of 18 is 9
19 is prime.
The for is one of the most versatile statements in the C# language because it allows a wide range of variations. They are examined here.
The for loop allows you to use two or more variables to control the loop. When using multiple loop control variables, the initialization and increment statements for each variable are separated by commas. Here is an example:
// Use commas in a for statement.
using System;
class Comma {
static void Main() {
int i, j;
for(i=0, j=10; i < j; i++, j--)
Console.WriteLine("i and j: " + i + " " + j);
}
}
The output from the program is shown here:
i and j: 0 10
i and j: 1 9
i and j: 2 8
i and j: 3 7
i and j: 4 6
Here, commas separate the two initialization statements and the two iteration expressions. When the loop begins, both i and j are initialized. Each time the loop repeats, i is incremented and j is decremented. Multiple loop control variables are often convenient and can simplify certain algorithms. You can have any number of initialization and iteration statements, but in practice, more than two make the for loop unwieldy.
Here is a practical use of multiple loop control variables in a for statement. This program uses two loop control variables within a single for loop to find the largest and smallest factor of a number, in this case 100. Pay special attention to the termination condition. It relies on both loop control variables.
// Use commas in a for statement to find the largest and
// smallest factor of a number.
using System;
class Comma {
static void Main() {
int i, j;
int smallest, largest;
int num;
num = 100;
smallest = largest = 1;
for(i=2, j=num/2; (i <= num/2) & (j >= 2); i++, j--) {
if((smallest == 1) & ((num % i) == 0))
smallest = i;
if((largest == 1) & ((num % j) == 0))
largest = j;
}
Console.WriteLine("Largest factor: " + largest);
Console.WriteLine("Smallest factor: " + smallest);
}
}
Here is the output from the program:
Largest factor: 50
Smallest factor: 2
Through the use of two loop control variables, a single for loop can find both the smallest and the largest factor of a number. The control variable i is used to search for the smallest factor. It is initially set to 2 and incremented until its value exceeds one half of num. The control variable j is used to search for the largest factor. Its value is initially set to one half the num and decremented until it is less than 2. The loop runs until both i and j are at their termination values. When the loop ends, both factors will have been found.
The conditional expression controlling a for loop can be any valid expression that produces a bool result. It does not need to involve the loop control variable. For example, in the next program, the for loop is controlled by the value of done.
// Loop condition can be any bool expression.
using System;
class forDemo {
static void Main() {
int i, j;
bool done = false;
for(i=0, j=100; !done; i++, j--) {
if(i*i >= j) done = true;
Console.WriteLine("i, j: " + i + " " + j);
}
}
}
The output is shown here:
i, j: 0 100
i, j: 1 99
i, j: 2 98
i, j: 3 97
i, j: 4 96
i, j: 5 95
i, j: 6 94
i, j: 7 93
i, j: 8 92
i, j: 9 91
i, j: 10 90
In this example, the for loop iterates until the bool variable done is true. This variable is set to true inside the loop when i squared is greater than or equal to j.
Some interesting for loop variations are created by leaving pieces of the loop definition empty. In C#, it is possible for any or all of the initialization, condition, or iteration portions of the for loop to be empty. For example, consider the following program:
// Parts of the for can be empty.
using System;
class Empty {
static void Main() {
int i;
for(i = 0; i < 10; ) {
Console.WriteLine("Pass #" + i);
i++; // increment loop control var
}
}
}
Here, the iteration expression of the for is empty. Instead, the loop control variable i is incremented inside the body of the loop. This means that each time the loop repeats, i is tested to see whether it equals 10, but no further action takes place. Of course, since i is incremented within the body of the loop, the loop runs normally, displaying the following output:
Pass #0
Pass #1
Pass #2
Pass #3
Pass #4
Pass #5
Pass #6
Pass #7
Pass #8
Pass #9
In the next example, the initialization portion is also moved out of the for:
// Move more out of the for loop.
using System;
class Empty2 {
static void Main() {
int i;
i = 0; // move initialization out of loop
for(; i < 10; ) {
Console.WriteLine("Pass #" + i);
i++; // increment loop control var
}
}
}
In this version, i is initialized before the loop begins, rather than as part of the for. Normally, you will want to initialize the loop control variable inside the for. Placing the initialization outside of the loop is generally done only when the initial value is derived through a complex process that does not lend itself to containment inside the for statement.
You can create an infinite loop (a loop that never terminates) using the for by leaving the conditional expression empty. For example, the following fragment shows the way many C# programmers create an infinite loop:
for(;;) // intentionally infinite loop
{
//...
}
This loop will run forever. Although there are some programming tasks, such as operating system command processors, that require an infinite loop, most “infinite loops” are really just loops with special termination requirements. (See “Using break to Exit a Loop,” later in this chapter.)
In C#, the body associated with a for loop (or any other loop) can be empty. This is because an empty statement is syntactically valid. Bodyless loops are often useful. For example, the following program uses a bodyless loop to sum the numbers 1 through 5:
// The body of a loop can be empty.
using System;
class Empty3 {
static void Main() {
int i;
int sum = 0;
// Sum the numbers through 5.
for(i = 1; i <= 5; sum += i++) ;
Console.WriteLine("Sum is " + sum);
}
}
The output from the program is shown here:
Sum is 15
Notice that the summation process is handled entirely within the for statement, and no body is needed. Pay special attention to the iteration expression:
sum += i++
Don’t be intimidated by statements like this. They are common in professionally written C# programs and are easy to understand if you break them down into their parts. In words, this statement says “add to sum the value of sum plus i, then increment i.” Thus, it is the same as this sequence of statements:
sum = sum + i;
i++;
Often the variable that controls a for loop is needed only for the purposes of the loop and is not used elsewhere. When this is the case, it is possible to declare the variable inside the initialization portion of the for. For example, the following program computes both the summation and the factorial of the numbers 1 through 5. It declares its loop control variable i inside the for:
// Declare loop control variable inside the for.
using System;
class ForVar {
static void Main() {
int sum = 0;
int fact = 1;
// Compute the factorial of the numbers 1 through 5.
for(int i = 1; i <= 5; i++) {
sum += i; // i is known throughout the loop.
fact *= i;
}
// But, i is not known here.
Console.WriteLine("Sum is " + sum);
Console.WriteLine("Factorial is " + fact);
}
}
When you declare a variable inside a for loop, there is one important point to remember: The scope of that variable ends when the for statement does. (That is, the scope of the variable is limited to the for loop.) Outside the for loop, the variable will cease to exist. Thus, in the preceding example, i is not accessible outside the for loop. If you need to use the loop control variable elsewhere in your program, you will not be able to declare it inside the for loop.
Before moving on, you might want to experiment with your own variations on the for loop. As you will find, it is a fascinating loop.
Another of C#’s loops is the while. The general form of the while loop is
while(condition) statement;
where statement can be a single statement or a block of statements, and condition defines the condition that controls the loop and may be any valid Boolean expression. The statement is performed while the condition is true. When the condition becomes false, program control passes to the line immediately following the loop.
Here is a simple example in which a while is used to compute the order of magnitude of an integer:
// Compute the order of magnitude of an integer.
using System;
class WhileDemo {
static void Main() {
int num;
int mag;
num = 435679;
mag = 0;
Console.WriteLine("Number: " + num);
while(num > 0) {
mag++;
num = num / 10;
};
Console.WriteLine("Magnitude: " + mag);
}
}
The output is shown here:
Number: 435679
Magnitude: 6
The while loop works like this: The value of num is tested. If num is greater than 0, the mag counter is incremented, and num is divided by 10. As long as the value in num is greater than 0, the loop repeats. When num is 0, the loop terminates and mag contains the order of magnitude of the original value.
As with the for loop, the while checks the conditional expression at the top of the loop, which means that the loop code may not execute at all. This eliminates the need for performing a separate test before the loop. The following program illustrates this characteristic of the while loop. It computes the integer powers of 2 from 0 to 9.
// Compute integer powers of 2.
using System;
class Power {
static void Main() {
int e;
int result;
for(int i=0; i < 10; i++) {
result = 1;
e = i;
while(e > 0) {
result *= 2;
e--;
}
Console.WriteLine("2 to the " + i + " power is " + result);
}
}
}
The output from the program is shown here:
2 to the 0 power is 1
2 to the 1 power is 2
2 to the 2 power is 4
2 to the 3 power is 8
2 to the 4 power is 16
2 to the 5 power is 32
2 to the 6 power is 64
2 to the 7 power is 128
2 to the 8 power is 256
2 to the 9 power is 512
Notice that the while loop executes only when e is greater than 0. Thus, when e is 0, as it is in the first iteration of the for loop, the while loop is skipped.
The third C# loop is the do-while. Unlike the for and the while loops, in which the condition is tested at the top of the loop, the do-while loop checks its condition at the bottom of the loop. This means that a do-while loop will always execute at least once. The general form of the do-while loop is
do {
statements;
} while(condition);
Although the braces are not necessary when only one statement is present, they are often used to improve readability of the do-while construct, thus preventing confusion with the while. The do-while loop executes as long as the conditional expression is true.
The following program uses a do-while loop to display the digits of an integer in reverse order:
// Display the digits of an integer in reverse order.
using System;
class DoWhileDemo {
static void Main() {
int num;
int nextdigit;
num = 198;
Console.WriteLine("Number: " + num);
Console.Write("Number in reverse order: ");
do {
nextdigit = num % 10;
Console.Write(nextdigit);
num = num / 10;
}while(num > 0);
Console.WriteLine();
}
}
The output is shown here:
Number: 198
Number in reverse order: 891
Here is how the loop works: With each iteration, the leftmost digit is obtained by computing the remainder of an integer division by 10. This digit is then displayed. Next, the value in num is divided by 10. Since this is an integer division, this results in the leftmost digit being removed. This process repeats until num is 0.
The foreach loop cycles through the elements of a collection. A collection is a group of objects. C# defines several types of collections, of which one is an array. The foreach loop is examined in Chapter 7, when arrays are discussed.
It is possible to force an immediate exit from a loop, bypassing any code remaining in the body of the loop and the loop’s conditional test, by using the break statement. When a break statement is encountered inside a loop, the loop is terminated, and program control resumes at the next statement following the loop. Here is a simple example:
// Using break to exit a loop.
using System;
class BreakDemo {
static void Main() {
// Use break to exit this loop.
for(int i=-10; i <= 10; i++) {
if(i > 0) break; // terminate loop when i is positive
Console.Write(i + " ");
}
Console.WriteLine("Done");
}
}
This program generates the following output:
-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 Done
As you can see, although the for loop is designed to run from –10 to 10, the break statement causes it to terminate early, when i becomes positive.
The break statement can be used with any of C#’s loops. For example, here is the previous program recoded to use a do-while loop:
// Using break to exit a do-while loop.
using System;
class BreakDemo2 {
static void Main() {
int i;
i = -10;
do {
if(i > 0) break;
Console.Write(i + " ");
i++;
} while(i <= 10);
Console.WriteLine("Done");
}
}
Here is a more practical example of break. This program finds the smallest factor of a number.
// Find the smallest factor of a value.
using System;
class FindSmallestFactor {
static void Main() {
int factor = 1;
int num = 1000;
for(int i=2; i <= num/i; i++) {
if((num%i) == 0) {
factor = i;
break; // stop loop when factor is found
}
}
Console.WriteLine("Smallest factor is " + factor);
}
}
The output is shown here:
Smallest factor is 2
The break stops the for loop as soon as a factor is found. The use of break in this situation prevents the loop from trying any other values once a factor has been found, thus preventing inefficiency.
When used inside a set of nested loops, the break statement will break out of only the innermost loop. For example:
// Using break with nested loops.
using System;
class BreakNested {
static void Main() {
for(int i=0; i<3; i++) {
Console.WriteLine("Outer loop count: " + i);
Console.Write(" Inner loop count: ");
int t = 0;
while(t < 100) {
if(t == 10) break; // terminate loop if t is 10
Console.Write(t + " ");
t++;
}
Console.WriteLine();
}
Console.WriteLine("Loops complete.");
}
}
This program generates the following output:
Outer loop count: 0
Inner loop count: 0 1 2 3 4 5 6 7 8 9
Outer loop count: 1
Inner loop count: 0 1 2 3 4 5 6 7 8 9
Outer loop count: 2
Inner loop count: 0 1 2 3 4 5 6 7 8 9
Loops complete.
As you can see, the break statement in the inner loop causes only the termination of that loop. The outer loop is unaffected.
Here are two other points to remember about break: First, more than one break statement may appear in a loop. However, be careful. Too many break statements have the tendency to destructure your code. Second, the break that exits a switch statement affects only that switch statement and not any enclosing loops.
It is possible to force an early iteration of a loop, bypassing the loop’s normal control structure. This is accomplished using continue. The continue statement forces the next iteration of the loop to take place, skipping any code in between. Thus, continue is essentially the complement of break. For example, the following program uses continue to help print the even numbers between 0 and 100.
// Use continue.
using System;
class ContDemo {
static void Main() {
// Print even numbers between 0 and 100.
for(int i = 0; i <= 100; i++) {
if((i%2) != 0) continue; // iterate
Console.WriteLine(i);
}
}
}
Only even numbers are printed, because an odd number will cause the loop to iterate early, bypassing the call to WriteLine( ).
In while and do-while loops, a continue statement will cause control to go directly to the conditional expression and then continue the looping process. In the case of the for, the iteration expression of the loop is evaluated, then the conditional expression is executed, and then the loop continues.
Good uses of continue are rare. One reason is that C# provides a rich set of loop statements that fit most applications. However, for those special circumstances in which early iteration is needed, the continue statement provides a structured way to accomplish it.
The return statement causes a method to return. It can also be used to return a value. It is examined in Chapter 6.
The goto is C#’s unconditional jump statement. When encountered, program flow jumps to the location specified by the goto. The statement fell out of favor with programmers many years ago because it encouraged the creation of “spaghetti code.” However, the goto is still occasionally—and sometimes effectively—used. This book will not make a judgment regarding its validity as a form of program control. It should be stated, however, that there are no programming situations that require the use of the goto statement—it is not necessary for making the language complete. Rather, goto is a convenience that, if used wisely, can be a benefit in certain programming situations. As such, the goto is not used in this book outside of this section. The chief concern most programmers have about the goto is its tendency to clutter a program and render it nearly unreadable. However, there are times when the use of the goto can clarify program flow rather than confuse it.
The goto requires a label for operation. A label is a valid C# identifier followed by a colon. The label must be in the same method as the goto that uses it and within scope. For example, a loop from 1 to 100 could be written using a goto and a label, as shown here:
x = 1;
loop1:
x++;
if(x < 100) goto loop1;
The goto can also be used to jump to a case or default statement within a switch. Technically, the case and default statements of a switch are labels. Thus, they can be targets of a goto. However, the goto statement must be executed from within the switch. That is, you cannot use the goto to jump into a switch statement. Here is an example that illustrates goto with a switch:
// Use goto with a switch.
using System;
class SwitchGoto {
static void Main() {
for(int i=1; i < 5; i++) {
switch(i) {
case 1:
Console.WriteLine("In case 1");
goto case 3;
case 2:
Console.WriteLine("In case 2");
goto case 1;
case 3:
Console.WriteLine("In case 3");
goto default;
default:
Console.WriteLine("In default");
break;
}
Console.WriteLine();
}
// goto case 1; // Error! Can't jump into a switch.
}
}
The output from the program is shown here:
In case 1
In case 3
In default
In case 2
In case 1
In case 3
In default
In case 3
In default
In default
Inside the switch, notice how the goto is used to jump to other case statements or the default statement. Furthermore, notice that the case statements do not end with a break. Since the goto prevents one case from falling through to the next, the no fall-through rule is not violated, and there is no need for a break statement. As explained, it is not possible to use the goto to jump into a switch. If you remove the comment symbols from the start of this line
// goto case 1; // Error! Can't jump into a switch.
the program will not compile. Frankly, using a goto with a switch can be useful in some special-case situations, but it is not recommended style in general.
One good use for the goto is to exit from a deeply nested routine. Here is a simple example:
// Demonstrate the goto.
using System;
class Use_goto {
static void Main() {
int i=0, j=0, k=0;
for(i=0; i < 10; i++) {
for(j=0; j < 10; j++ ) {
for(k=0; k < 10; k++) {
Console.WriteLine("i, j, k: " + i + " " + j + " " + k);
if(k == 3) goto stop;
}
}
}
stop:
Console.WriteLine("Stopped! i, j, k: " + i + ", " + j + " " + k);
}
}
The output from the program is shown here:
i, j, k: 0 0 0
i, j, k: 0 0 1
i, j, k: 0 0 2
i, j, k: 0 0 3
Stopped! i, j, k: 0, 0 3
Eliminating the goto would force the use of three if and break statements. In this case, the goto simplifies the code. While this is a contrived example used for illustration, you can probably imagine situations in which a goto might be beneficial.
One last point: Although you can jump out of a block (as the preceding example shows), you can’t use the goto to jump into a block.