5.2. Conditional Branching StatementsAlthough methods branch unconditionally, often you will want to branch within a method depending on a condition that you evaluate while the program is running. This is known as conditional branching . Conditional branching statements allow you to write logic such as, "If you are over 25 years old, then you may rent a car." C# provides a number of constructs that allow you to write conditional branches into your programs; these constructs are described in the following sections. 5.2.1. if StatementsThe simplest branching statement is if . An if statement says, "if a particular condition is true, then execute the statement; otherwise , skip it." The condition is a Boolean expression . An expression is a statement that evaluates to a value, and a Boolean expression evaluates to either true or false. The formal description of an if statement is: if ( expression ) Statement1 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 an expression (a statement that returns a value) in parentheses, and executes Statement1 if the expression evaluates true. Note that Statement1 can actually be a block of statements within braces, as illustrated in Example 5-2. Example 5-2. The if statement
In this simple program, you declare three variables , valueOne , valueTwo , and valueThree , with the values 10, 20, and 30, respectively. In the first if statement, you test whether valueOne is greater than valueTwo : if ( valueOne > valueTwo ) { Console.WriteLine( "ValueOne: {0} larger than ValueTwo: {1}", valueOne, valueTwo ); } Because valueOne (10) is less than valueTwo (20), this if statement fails (the condition returns false), and thus the body of the if statement (the statements within the braces) doesn't execute. You then test whether valueThree is greater than valueTwo : if ( valueThree > valueTwo ) { Console.WriteLine( "ValueThree: {0} larger than ValueTwo: {1}", valueThree, valueTwo ); } // end if Because valueThree (30) is greater than valueTwo (20), the test returns true, and thus the statement executes. The statement in this case is the block in which you call the WriteLine( ) method, shown in bold. The output reflects that the first if fails but the second succeeds: Testing valueOne against valueTwo... Testing valueThree against valueTwo... ValueThree: 30 larger than ValueTwo: 20 5.2.2. Single-Statement if BlocksNotice that the if statement blocks shown in Example 5-2 each contain only a single statement, one call to WriteLine( ) . In such cases, you can leave out the braces enclosing the if block. Thus, you might rewrite Example 5-2, as shown in Example 5-3. Example 5-3. Single statements with if
It is generally a good idea, however, to use the braces even when your if block has only a single statement. There are two reasons for this advice. First, the code is somewhat easier to read and understand with the braces. Code that is easier to read is easier to maintain.
The second reason for using braces is to avoid a common error: adding a second statement to the if and forgetting to add the braces. Consider the code shown in Example 5-4. The programmer has changed the value of valueThree to 10 and added a second statement to the second if block, as shown in bold. Example 5-4. Adding a second statement to if
Now, before reading any further, review the code and decide for yourself what the output should be. Don't cheat by looking past this paragraph. Then, when you think you know what the output will be, take a look at this: Testing valueOne against valueTwo... Testing valueThree against valueTwo... Good thing you tested again! Were you surprised? The programmer was fooled by the lack of braces and the indentation. Remember that indentation is whitespace and is ignored by the compiler. From the perspective of the programmer, the second statement ("Good thing ...") is part of the if block: if ( valueThree > valueTwo ) Console.WriteLine( "ValueThree: {0} larger than ValueTwo: {1}", valueThree, valueTwo); Console.WriteLine("Good thing you tested again!"); The compiler, however, considers only the first statement after the if test to be part of the if statement. The second statement is not part of the if statement. To the compiler, the if statement looks like this: if ( valueThree > valueTwo ) Console.WriteLine( "ValueThree: {0} larger than ValueTwo: {1}", valueThree, valueTwo); Console.WriteLine("Good thing you tested again!"); If you want the second statement to be part of the if statement, you must use braces , as in the following: if ( valueThree > valueTwo ) { Console.WriteLine( "ValueThree: {0} larger than ValueTwo: {1}", valueThree, valueTwo); Console.WriteLine("Good thing you tested again!"); } Because of this potential for confusion, many C# programmers use braces with every if statement, even if the statement is only one line.
5.2.3. Short-Circuit EvaluationConsider the following code snippet: int x = 8; int y = 15; if ((x == 8) (y == 12)) The if statement here is a bit complicated. The entire if statement is in parentheses, as are all if statements in C#. Thus, everything within the outer set of parentheses must evaluate true for the if statement to be true. Within the outer parentheses are two expressions, ( x == 8 ) and ( y == 12 ), which are separated by an or operator ( ). Because x is 8, the first term ( x == 8 ) evaluates true. There is no need to evaluate the second term ( y == 12 ). It doesn't matter whether y is 12; the entire expression will be true. Similarly, consider this snippet: int x = 8; int y = 12; if ((x == 5) && (y == 12)) Again, there is no need to evaluate the second term. Because the first term is false, the and must fail. (Remember, for an and statement to evaluate true, both tested expressions must evaluate true.) In cases such as these, the C# compiler will short-circuit the evaluation; the second test will never be performed. This allows you to create if statements in which you first check a value before you take action on it, avoiding the possibility of an exception. Here's a short example: public bool QuotientOverTwenty(float dividend, float divisor) { if ( divisor != 0 && dividend / divisor > 20 ) { return true; } return false; } In this code, we only want to decide if the quotient is greater than 20, but we must first make sure we are not dividing by zero (division by zero causes the system to throw an exception). With short circuiting, the second part of the if statement (the division) will never occur if the first part is false (that is, if the divisor is zero), and this code is terser and perhaps easier to understand than writing. public bool QuotientOverTwenty(float dividend, float divisor) { bool retVal = false; if ( divisor != 0 ) { if ( dividend / divisor > 20 ) retVal = true; } return retVal; } 5.2.4. if ... else StatementsOften, you will find that you want to take one set of actions when the condition tests true and a different set of actions when the condition tests false. This allows you to write logic such as, "If you are over 25 years old, then you may rent a car; otherwise , you must take the train." The otherwise portion of the logic is executed in the else statement. For example, you can modify Example 5-2 to print an appropriate message whether or not valueOne is greater than valueTwo , as shown in Example 5-5. Example 5-5. The else statement
The output looks like this: Testing valueOne against valueTwo... Nope, ValueOne: 10 is NOT larger than ValueTwo: 20 Because the test in the if statement fails ( valueOne is not larger than valueTwo ), the body of the if statement is skipped and the body of the else statement is executed. Had the test succeeded, the if statement body would execute and the else statement would be skipped. 5.2.5. Nested if StatementsIt 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 5-6 illustrates one approach using nested if statements. Example 5-6. Nested if statements
The logic of Example 5-6 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 next message. Notice that this second if statement is nested within the first if , so the logic of the else statement is: "because 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." Another way of chaining together more than one possibility with if statements is the else if idiom that some C# programmers use. The program tests the condition in the first if statement. If that first statement is false, control passes to the else statement, which is immediately followed by another if that tests a different condition. For example, you could rewrite Example 5-6 to test if the temperature is greater than, less than, or exactly equal to freezing with three tests, as shown in Example 5-7. Example 5-7. Using else if
In this case, the condition in the first if statement tests whether temp is less than 32, not less than or equal. Because temp is hard-wired to exactly 32, the first expression is false, and control passes to the else if statement. The second statement is true, so the third case, the else statement, never executes. Please note, however, that this code is identical (as far as the compiler is concerned ) to the following: using System; class Values { static void Main( ) { int temp = 32; if ( temp < 32 ) { Console.WriteLine( "Warning! Ice on road!" ); } else { if ( temp == 32 ) { Console.WriteLine("Temp exactly freezing, beware of water." ); } else { Console.WriteLine( "Watch for black ice! Temp: {0}", temp ); } } } } In any case, if you do use the else if idiom, be sure to use an else , (not an else if) , as your final test, making it the default case that will execute even if nothing else does. 5.2.6. switch StatementsNested 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 this: "Pick a matching value and act accordingly ." switch ( expression ) { case constant-expression : statement jump-statement [ default: statement ] } The expression you are "switching on" is put in parentheses in the head of the switch statement. Each case statement compares a constant value with the expression. The constant expression can be a literal, symbolic, or enumerated constant. The compiler starts with the first case statement and works its way down the list, looking for a value that matches the expression. If a case is matched, the statement (or block of statements) associated with that case is executed. The case block must end with a jump statement. Typically, the jump statement is break , which abruptly ends the entire switch statement. When you execute a break in a switch statement, execution continues after the closing brace of the switch statement. (We'll consider the use of the optional default keyword later in this section.) In the next, somewhat whimsical listing (Example 5-8), the user is asked to choose his political affiliation among Democrat, Republican, or Progressive. To keep the code simple, I'll hardwire the choice to be Democrat. Example 5-8. Using a switch statement
The output looks like this: You voted Democratic. Thank you for voting. Rather than using a complicated if statement, Example 5-8 uses a switch statement. The user's choice is evaluated in the head of the switch statement, and the block of statements that gets executed depends on whatever case matches (in this instance, Democrat). The statements between the case statement and the break are executed in series. You can have more than one statement here without braces; in effect, the case statement and the closing break statement act as the braces. It is possible that the user will not make a choice among Democrat, Republican, and Progressive. You may want to provide a default case that will be executed whenever no valid choice has been made. You can do that with the default keyword, as shown in Example 5-9. Example 5-9. A default statement
The output looks like this: You did not make a valid choice. Thank you for voting. If the user does not choose one of the values that correspond to a case statement, the default statements will execute. In this case, a message is simply printed telling the user he did not make a valid choice; in production code, you would put all this in a while loop, re-prompting the user until a valid choice is made (or the user elects to quit). 5.2.7. Falling-Through and Jumping-to CasesIf two cases will execute the same code, you can create what's known as a "fall through" case, grouping the case statements together with the same code, as shown here: case CompassionateRepublican: case Republican: Console.WriteLine("You voted Republican.\n"); Console.WriteLine("Don't you feel compassionate?"); break; In this example, if the user chooses either CompassionateRepublican or Republican, the same set of statements will be executed. Note that you can only fall through if the first case executes no code. In this example, the first case, CompassionateRepublican, meets that criteria. Thus, you can fall through to the second case. If, however, you want to execute a statement with one case and then fall through to the next, you must use the goto keyword to jump to the next case you want to execute.
For example, if you create a NewLeft party, you might want the NewLeft voting choice to print a message and then fall through to Democrat (that is, continue on with the statements in the Democrat case). You might (incorrectly) try writing the following: case NewLeft: Console.WriteLine("The NewLeft members are voting Democratic."); case Democrat: Console.WriteLine("You voted Democratic.\n"); break; This code will not compile; it will fail with the error: Control cannot fall through from one case label (case '4:') to another This is a potentially misleading error message. Control can fall through from one case label to another, but only if there is no code in the first case label.
Because the NewLeft case has a statement, the WriteLine( ) method, you must use a goto statement to fall through: case NewLeft: Console.WriteLine("The NewLeft members are voting Democratic."); goto case Democrat; case Democrat: Console.WriteLine("You voted Democratic.\n"); break; This code will compile and execute as you expect.
5.2.8. Switch on string StatementsIn the previous example, the switch value was an integral constant. C# also offers the ability to switch on a string . Thus, you can rewrite Example 5-9 to switch on the string "NewLeft," as in Example 5-10. Example 5-10. Switching on a string
|