C# Programming: From Problem Analysis to Program Design
In C# a complete program instruction is called a statement. Programs consist of sequences of C# statements. Each statement must end with a semicolon (;). For example: int x; // a statement x = 23; // another statement int y = x; // yet another statement C# statements are evaluated in order. The compiler starts at the beginning of a statement list and makes its way to the bottom. This would be entirely straightforward, and terribly limiting, were it not for branching. There are two types of branches in a C# program: unconditional branching and conditional branching. Program flow is also affected by looping and iteration statements, which are signaled by the keywords for, while, do, in, and foreach. Iteration is discussed later in this chapter. For now, let's consider some of the more basic methods of conditional and unconditional branching. 3.5.1 Unconditional Branching Statements
An unconditional branch is created in one of two ways. The first way is by invoking a method. When the compiler encounters the name of a method, it stops execution in the current method and branches to the newly "called" method. When that method returns a value, execution picks up in the original method on the line just below the method call. Example 3-6 illustrates. Example 3-6. Calling a method
using System; class Functions { static void Main( ) { Console.WriteLine("In Main! Calling SomeMethod( )..."); SomeMethod( ); Console.WriteLine("Back in Main( )."); } static void SomeMethod( ) { Console.WriteLine("Greetings from SomeMethod!"); } } Output: In Main! Calling SomeMethod( )... Greetings from SomeMethod! Back in Main( ). Program flow begins in Main( ) and proceeds until SomeMethod( ) is invoked (invoking a method is sometimes referred to as "calling" the method). At that point, program flow branches to the method. When the method completes, program flow resumes at the next line after the call to that method. The second way to create an unconditional branch is with one of the unconditional branch keywords: goto, break, continue, return, or throw. Additional information about the first three jump statements is provided later in this chapter; the final statement, throw, is discussed in Chapter 11. 3.5.2 Conditional Branching Statements
A conditional branch is created by a conditional statement, which is signaled by keywords such as if, else, or switch. A conditional branch occurs only if the condition expression evaluates true.
3.5.2.1 If...else statements
If...else statements branch based on a condition. The condition is an expression, tested in the head of the if statement. If the condition evaluates true, the statement (or block of statements) in the body of the if statement is executed. If statements may contain an optional else statement. The else statement is executed only if the expression in the head of the if statement evaluates false: if (expression ) statement1 [else statement2 ] This is the kind of description of the if statement you are likely to find in your compiler documentation. It shows you that the if statement takes a Boolean expression (an expression that evaluates true or false) in parentheses, and executes statement1 if the expression evaluates true. Note that statement1 can actually be a block of statements within braces. You can also see that the else statement is optional, as it is enclosed in square brackets. Although this gives you the syntax of an if statement, an illustration will make its use clear. See Example 3-7. Example 3-7. If ... else statements
using System; class Values { static void Main( ) { int valueOne = 10; int valueTwo = 20; if ( valueOne > valueTwo ) { Console.WriteLine( "ValueOne: {0} larger than ValueTwo: {1}", valueOne, valueTwo); } else { Console.WriteLine( "ValueTwo: {0} larger than ValueOne: {1}", valueTwo,valueOne); } valueOne = 30; // set valueOne higher if ( valueOne > valueTwo ) { valueTwo = valueOne++; Console.WriteLine("\nSetting valueTwo to valueOne value, "); Console.WriteLine("and incrementing ValueOne.\n"); Console.WriteLine("ValueOne: {0} ValueTwo: {1}", valueOne, valueTwo); } else { valueOne = valueTwo; Console.WriteLine("Setting them equal. "); Console.WriteLine("ValueOne: {0} ValueTwo: {1}", valueOne, valueTwo); } } } In Example 3-7, the first if statement tests whether valueOne is greater than valueTwo. The relational operators such as greater than (>), less than (<), and equal to (==) are fairly intuitive to use. The test of whether valueOne is greater than valueTwo evaluates false (because valueOne is 10 and valueTwo is 20, so valueOne is not greater than valueTwo). The else statement is invoked, printing the statement: ValueTwo: 20 is larger than ValueOne: 10 The second if statement evaluates true and all the statements in the if block are evaluated, causing two lines to print: Setting valueTwo to valueOne value, and incrementing ValueOne. ValueOne: 31 ValueTwo: 30
3.5.2.2 Nested if statements
It is possible, and not uncommon, to nest if statements to handle complex conditions. For example, suppose you need to write a program to evaluate the temperature, and specifically to return the following types of information:
There are many good ways to write this program. Example 3-8 illustrates one approach, using nested if statements. Example 3-8. Nested if statements
using System; class Values { static void Main( ) { int temp = 32; if (temp <= 32) { Console.WriteLine("Warning! Ice on road!"); if (temp == 32) { Console.WriteLine( "Temp exactly freezing, beware of water."); } else { Console.WriteLine("Watch for black ice! Temp: {0}", temp); } } } } The logic of Example 3-8 is that it tests whether the temperature is less than or equal to 32. If so, it prints a warning: if (temp <= 32) { Console.WriteLine("Warning! Ice on road!"); The program then checks whether the temp is equal to 32 degrees. If so, it prints one message; if not, the temp must be less than 32 and the program prints the second message. Notice that this second if statement is nested within the first if, so the logic of the else is "since it has been established that the temp is less than or equal to 32, and it isn't equal to 32, it must be less than 32."
3.5.2.3 Switch statements: an alternative to nested ifs
Nested if statements are hard to read, hard to get right, and hard to debug. When you have a complex set of choices to make, the switch statement is a more powerful alternative. The logic of a switch statement is "pick a matching value and act accordingly." switch (expression ) { case constant-expression : statement jump-statement [default: statement ] } As you can see, like an if statement, the expression is put in parentheses in the head of the switch statement. Each case statement then requires a constant expression; that is, a literal or symbolic constant or an enumeration. If a case is matched, the statement (or block of statements) associated with that case is executed. This must be followed by a jump statement. Typically, the jump statement is break, which transfers execution out of the switch. An alternative is a goto statement, typically used to jump into another case, as illustrated in Example 3-9. Example 3-9. The switch statement
using System; class Values { static void Main( ) { const int Democrat = 0; const int LiberalRepublican = 1; const int Republican = 2; const int Libertarian = 3; const int NewLeft = 4; const int Progressive = 5; int myChoice = Libertarian; switch (myChoice) { case Democrat: Console.WriteLine("You voted Democratic.\n"); break; case LiberalRepublican: // fall through //Console.WriteLine( //"Liberal Republicans vote Republican\n"); case Republican: Console.WriteLine("You voted Republican.\n"); break; case NewLeft: Console.WriteLine("NewLeft is now Progressive"); goto case Progressive; case Progressive: Console.WriteLine("You voted Progressive.\n"); break; case Libertarian: Console.WriteLine("Libertarians are voting Republican"); goto case Republican; default: Console.WriteLine("You did not pick a valid choice.\n"); break; } Console.WriteLine("Thank you for voting."); } } In this whimsical example, we create constants for various political parties. We then assign one value (Libertarian) to the variable myChoice and switch on that value. If myChoice is equal to Democrat, we print out a statement. Notice that this case ends with break. break is a jump statement that takes us out of the switch statement and down to the first line after the switch, on which we print "Thank you for voting."
The value LiberalRepublican has no statement under it, and it "falls through" to the next statement: Republican. If the value is LiberalRepublican or Republican, the Republican statements execute. You can only "fall through" in this way if there is no body within the statement. If you uncomment the WriteLine( ) under LiberalRepublican, this program will not compile.
If you do need a statement but you then want to execute another case, you can use the goto statement, as shown in the NewLeft case: goto case Progressive; It is not required that the goto take you to the case immediately following. In the next instance, the Libertarian choice also has a goto, but this time it jumps all the way back up to the Republican case. Because our value was set to Libertarian, this is just what occurs. We print out the Libertarian statement, go to the Republican case, print that statement, and then hit the break, taking us out of the switch and down to the final statement. The output for all of this is: Libertarians are voting Republican You voted Republican. Thank you for voting. Note the default case, excerpted from Example 3-9: default: Console.WriteLine( "You did not pick a valid choice.\n"); If none of the cases match, the default case will be invoked, warning the user of the mistake. 3.5.2.4 Switch on string statements
In the previous example, the switch value was an integral constant. C# offers the ability to switch on a string, allowing you to write: case "Libertarian": If the strings match, the case statement is entered. 3.5.3 Iteration Statements
C# provides an extensive suite of iteration statements, including for, while and do...while loops, as well as foreach loops (new to the C family but familiar to VB programmers). In addition, C# supports the goto, break, continue, and return jump statements. 3.5.3.1 The goto statement
The goto statement is the seed from which all other iteration statements have been germinated. Unfortunately, it is a semolina seed, producer of spaghetti code and endless confusion. Most experienced programmers properly shun the goto statement, but in the interest of completeness, here's how you use it:
The label is an identifier followed by a colon. The goto command is typically tied to a condition, as illustrated in Example 3-10. Example 3-10. Using goto
using System; public class Tester { public static int Main( ) { int i = 0; repeat: // the label Console.WriteLine("i: {0}",i); i++; if (i < 10) goto repeat; // the dastardly deed return 0; } } If you were to try to draw the flow of control in a program that makes extensive use of goto statements, the resulting morass of intersecting and overlapping lines looks like a plate of spaghetti; hence the term "spaghetti code." It was this phenomenon that led to the creation of alternatives, such as the while loop. Many programmers feel that using goto in anything other than a trivial example creates confusion and difficult-to-maintain code. 3.5.3.2 The while loop
The semantics of the while loop are "while this condition is true, do this work." The syntax is: while (expression) statement As usual, an expression is any statement that returns a value. While statements require an expression that evaluates to a Boolean (true/false) value, and that statement can, of course, be a block of statements. Example 3-11 updates Example 3-10, using a while loop. Example 3-11. Using a while loop
using System; public class Tester { public static int Main( ) { int i = 0; while (i < 10) { Console.WriteLine("i: {0}",i); i++; } return 0; } } The code in Example 3-11 produces results identical to the code in Example 3-10, but the logic is a bit clearer. The while statement is nicely self-contained, and it reads like an English sentence: "while i is less than 10, print this message and increment i." Notice that the while loop tests the value of i before entering the loop. This ensures that the loop will not run if the condition tested is false; thus if i is initialized to 11, the loop will never run. 3.5.3.3 The do...while loop
There are times when a while loop might not serve your purpose. In certain situations, you might want to reverse the semantics from "run while this is true" to the subtly different "do this while this condition remains true." In other words, take the action, and then, after the action is completed, check the condition. For this you will use the do...while loop. do statement while expression An expression is any statement that returns a value. Example 3-12 shows the do...while loop. Example 3-12. The do...while loop
using System; public class Tester { public static int Main( ) { int i = 11; do { Console.WriteLine("i: {0}",i); i++; } while (i < 10); return 0; } } Here i is initialized to 11 and the while test fails, but only after the body of the loop has run once. 3.5.3.4 The for loop
A careful examination of the while loop in Example 3-11 reveals a pattern often seen in iterative statements: initialize a variable (i = 0), test the variable (i < 10), execute a series of statements, and increment the variable (i++). The for loop allows you to combine all these steps in a single loop statement: for ([initializers]; [expression]; [iterators]) statement The for loop is illustrated in Example 3-13. Example 3-13. The for loop
using System; public class Tester { public static int Main( ) { for (int i=0;i<100;i++) { Console.Write("{0} ", i); if (i%10 == 0) { Console.WriteLine("\t{0}", i); } } return 0; } } Output: 0 0 1 2 3 4 5 6 7 8 9 10 10 11 12 13 14 15 16 17 18 19 20 20 21 22 23 24 25 26 27 28 29 30 30 31 32 33 34 35 36 37 38 39 40 40 41 42 43 44 45 46 47 48 49 50 50 51 52 53 54 55 56 57 58 59 60 60 61 62 63 64 65 66 67 68 69 70 70 71 72 73 74 75 76 77 78 79 80 80 81 82 83 84 85 86 87 88 89 90 90 91 92 93 94 95 96 97 98 99 This for loop makes use of the modulus operator described later in this chapter. The value of i is printed until i is a multiple of 10. if (i%10 == 0) A tab is then printed, followed by the value. Thus the 10s (20, 30, 40, etc.) are called out on the right side of the output.
The individual values are printed using Console.Write( ), which is much like WriteLine( ) but which does not enter a newline character, allowing the subsequent writes to occur on the same line. A few quick points to notice: in a for loop, the condition is tested before the statements are executed. Thus, in the example, i is initialized to zero, then it is tested to see if it is less than 100. Because i < 100 returns true, the statements within the for loop are executed. After the execution, i is incremented (i++). Note that the variable i is scoped to within the for loop (that is, the variable i is visible only within the for loop). Example 3-14 will not compile. Example 3-14. Scope of variables declared in a for loop
using System; public class Tester { public static int Main( ) { for (int i=0; i<100; i++) { Console.Write("{0} ", i); if ( i%10 == 0 ) { Console.WriteLine("\t{0}", i); } } Console.WriteLine("\n Final value of i: {0}", i); return 0; } } The line shown in bold fails, as the variable i is not available outside the scope of the for loop itself.
3.5.3.5 The foreach statement
The foreach statement is new to the C family of languages; it is used for looping through the elements of an array or a collection. Discussion of this incredibly useful statement is deferred until Chapter 9. 3.5.3.6 The continue and break statements
There are times when you would like to restart a loop without executing the remaining statements in the loop. The continue statement causes the loop to return to the top and continue executing. The obverse side of that coin is the ability to break out of a loop and immediately end all further work within the loop. For this purpose the break statement exists.
Example 3-15 illustrates the mechanics of continue and break. This code, suggested to me by one of my technical reviewers, Donald Xie, is intended to create a traffic signal processing system. The signals are simulated by entering numerals and uppercase characters from the keyboard, using Console.ReadLine( ), which reads a line of text from the keyboard. The algorithm is simple: receipt of a 0 (zero) means normal conditions, and no further action is required except to log the event. (In this case, the program simply writes a message to the console; a real application might enter a timestamped record in a database.) On receipt of an abort signal (here simulated with an uppercase "A"), the problem is logged and the process is ended. Finally, for any other event, an alarm is raised, perhaps notifying the police. (Note that this sample does not actually notify the police, though it does print out a harrowing message to the console.) If the signal is "X," the alarm is raised, but the while loop is also terminated. Example 3-15. Using continue and break
using System; public class Tester { public static int Main( ) { string signal = "0"; // initialize to neutral while (signal != "X") // X indicates stop { Console.Write("Enter a signal: "); signal = Console.ReadLine( ); // do some work here, no matter what signal you // receive Console.WriteLine("Received: {0}", signal); if (signal == "A") { // faulty - abort signal processing // Log the problem and abort. Console.WriteLine("Fault! Abort\n"); break; } if (signal == "0") { // normal traffic condition // log and continue on Console.WriteLine("All is well.\n"); continue; } // Problem. Take action and then log the problem // and then continue on Console.WriteLine("{0} -- raise alarm!\n", signal); } return 0; } } Output: Enter a signal: 0 Received: 0 All is well. Enter a signal: B Received: B B -- raise alarm! Enter a signal: A Received: A Fault! Abort Press any key to continue The point of this exercise is that when the A signal is received, the action in the if statement is taken and then the program breaks out of the loop, without raising the alarm. When the signal is 0, it is also undesirable to raise the alarm, so the program continues from the top of the loop. |