Although 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.” This is where the comparison operators you learned about in Chapter 4 become really useful.
C# provides a number of constructs that allow you to write conditional branches into your programs, including the if
, else
, and switch
statements.
The simplest branching statement is if
. An if
statement says, “If a particular condition is true, then execute the following statement; otherwise, skip it.” The condition is a Boolean expression. As you learned in Chapters Chapter 3 and Chapter 4, a Boolean expression evaluates to either true or false, which makes it a perfect fit for the if
statement.
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. Statement1
doesn’t have to be just one statement—it can actually be a block of statements within braces. As long as you include the braces, the compiler treats your code as just one statement. Example 5-2 shows how this works.
Example 5-2. The if statement evaluates an expression, and executes a statement if the expression is true, or skips it if the expression is false
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_2_ _ _ _The_if_Statement { class Program { static void Main( ) { int valueOne = 10; int valueTwo = 20; int valueThree = 30; Console.WriteLine("Testing valueOne against valueTwo..."); if (valueOne > valueTwo) { Console.WriteLine("ValueOne: {0} larger than ValueTwo: {1}", valueOne, valueTwo); } Console.WriteLine("Testing valueThree against valueTwo..."); if (valueThree > valueTwo) { Console.WriteLine("ValueThree: {0} larger than ValueTwo: {1}", valueThree, valueTwo); } } } }
Just about anywhere in C# that you are expected to provide a statement, you can instead provide a block of statements within braces. (See the “Brace Styles” sidebar in this chapter.)
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). Therefore, the body of the if
statement (the statements within the braces) doesn’t execute, and the WriteLine
never executes.
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
Notice 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. When your if statement block contains only a single statement, you can leave out the braces
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_3_ _ _ _if_Block_without_Braces { class Program { static void Main( ) { int valueOne = 10; int valueTwo = 20; int valueThree = 30; Console.WriteLine("Testing valueOne against valueTwo..."); if (valueOne > valueTwo) Console.WriteLine("ValueOne: {0} larger than ValueTwo: {1}", valueOne, valueTwo); Console.WriteLine("Testing valueThree against valueTwo..."); if (valueThree > valueTwo) Console.WriteLine("ValueThree: {0} larger than ValueTwo: {1}", valueThree, valueTwo); } } }
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.
When programmers talk about maintaining code, they mean either adding to the code as requirements change or fixing the code as bugs are discovered. You may find yourself maintaining code that you wrote months or years ago, or maintaining code somebody else wrote. In those cases, clear, readable code is a lifesaver.
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. When you add a second statement to an if block, be sure to enclose it in the braces, or you may get unexpected results
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_4_ _ _ _Adding_to_an_if_Block { class Program { static void Main( ) { int valueOne = 10; int valueTwo = 20;int valueThree = 10;
Console.WriteLine("Testing valueOne against valueTwo..."); if (valueOne > valueTwo) Console.WriteLine("ValueOne: {0} larger than ValueTwo: {1}", valueOne, valueTwo); Console.WriteLine("Testing valueThree against valueTwo..."); if (valueThree > valueTwo) Console.WriteLine("ValueThree: {0} larger than ValueTwo: {1}", valueThree, valueTwo);Console.WriteLine("Good thing you tested again!");
} } }
Now, before reading any further, review the code and decide 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. Indentation is whitespace, and as we mentioned in Chapter 3, whitespace is ignored by the compiler. From the perspective of the programmer, the second statement (“Good thing . . . “) looks to be 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.
Consider 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, because the first part is true. (Remember, for an or
statement to evaluate true
, just one of the expressions has to 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, you want to determine whether the quotient is greater than 20, but you must first make sure you 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).
Often, 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 follows 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. Adding an else statement to your if statement lets you take an action if the condition in the if statement is false—it’s the “or” to the if’s “either”
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_5_ _ _ _The_else_Statement { class Program { static void Main( ) { int valueOne = 10; int valueTwo = 20; Console.WriteLine("Testing valueOne against valueTwo..."); if (valueOne > valueTwo) { Console.WriteLine("ValueOne: {0} larger than ValueTwo: {1}", valueOne, valueTwo); } else { Console.WriteLine("Nope, ValueOne: {0} is NOT larger than ValueTwo: {1}", valueOne, valueTwo); } } } }
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.
You’ve seen how to make your if
statement take action for two possible options, but what if there are more than two choices? In that case, you can nest if
statements—that is, contain one if
inside another—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:
If the temperature is 32 degrees or lower, the program should warn you about ice on the road.
If the temperature is exactly 32 degrees, the program should tell you that there may be water on the road.
If the temperature is higher than 32 degrees, the program should assure you that there is no ice.
There are many good ways to write this program. Example 5-6 illustrates one approach using nested if
statements.
Example 5-6. You can nest if statements safely, one inside the other
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_6_ _ _ _Nested_if_Statements { class Program { 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); } } else { Console.WriteLine("No ice; drive with confidence."); } } } }
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 temperature is equal to 32 degrees. If so, it prints one message; if not, the temperature 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 temperature is less than or equal to 32, and it isn’t equal to 32, it must be less than 32.”
Another way you can chain together more than one possibility with if
statements is to use the else
if
idiom. 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 whether the temperature is greater than, less than, or exactly equal to freezing with three tests, as shown in Example 5-7.
Example 5-7. The else if construct is another way of chaining together if statements without nesting
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_7_ _ _ _else_if { class Program { 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("No ice; drive with confidence."); } } } }
In this case, the condition in the first if
statement tests whether temp
is less than 32, not less than or equal to 32. Because temp
is hardwired 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:
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("No ice; drive with confidence."); } } }
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 when nothing else does.
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 this: “pick a matching value and act accordingly.”
switch
(expression
) {case
constant-expression
:
statement
jump-statement
[default:
statement
] }
The expression you are testing (or “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 her political affiliation among Democrat, Republican, or Progressive. To keep the code simple, we’ll hard-wire the choice to be Democrat.
Example 5-8. Use a switch statement to compare a value to a set of constants, and take action accordingly; a switch statement is easier to use and more readable than nested if statements
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_8_ _ _ _The_switch_Statement { class Program { enum Party { Democrat, Republican, Progressive } static void Main( ) { // hardwire to Democratic Party myChoice = Party.Democrat; // switch on the value of myChoice switch (myChoice) { case Party.Democrat: Console.WriteLine("You voted Democratic."); break; case Party.Republican: Console.WriteLine("You voted Republican."); break; case Party.Progressive: Console.WriteLine("You voted Progressive."); break; } Console.WriteLine("Thank you for voting."); } } }
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.
We hardwired the choice here, but if you’re accepting user input instead, 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. This example already has some built-in safety, because you can’t hard-wire a choice that’s not a member of the enum
, and the switch
statement has a block for each possible member of the enum
. We’ve commented out the Democrat
choice, making it invalid, so that you can see the default
statement at work.
Example 5-9. The default statement gives you a backup that will always be executed, if the user doesn’t make a valid choice
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_9_ _ _ _The_default_statement { class Program { enum Party { Democrat, Republican, Progressive } static void Main( ) { // hardwire to Democratic Party myChoice = Party.Democrat; // switch on the value of myChoice switch (myChoice) { /* case Party.Democrat: Console.WriteLine("You voted Democratic."); break; */ case Party.Republican: Console.WriteLine("You voted Republican."); break; case Party.Progressive: Console.WriteLine("You voted Progressive."); break; default: Console.WriteLine("You did not make a valid choice."); break; } Console.WriteLine("Thank you for voting."); } } }
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
statement will execute. In this case, a message is simply printed telling the user she 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).
If 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. "); 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 fall through only if the first case executes no code. In this example, the first case, CompassionateRepublican
, meets that criterion. 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.
The goto
keyword is an unconditional branch. When the compiler sees this word, it immediately transfers the flow (jumps) to wherever the goto
points. Thus, even within this conditional branching statement, you’ve inserted an unconditional branch.
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, to 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. "); 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.
Notice that the error displays the name of the case with its numeric value (4) rather than its symbolic value (NewLeft
). Remember that NewLeft
is just the name of the constant. Behind the scenes of your enum
, the values look like this:
const int Democrat = 0; const int CompassionateRepublican = 1; const int Republican = 2; const int Progressive = 3; const int NewLeft = 4;
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. "); break;
This code will compile and execute as you expect.
In 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. You can switch on a string, as well as on an integral constant
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_5_10_ _ _ _Switching_on_a_String { class Program { static void Main( ) { String myChoice = "NewLeft"; // switch on the string value of myChoice switch (myChoice) { case "NewLeft": Console.WriteLine( "The NewLeft members are voting Democratic."); goto case "Democrat"; case "Democrat": Console.WriteLine("You voted Democratic. "); break; case "CompassionateRepublican": // fall through case "Republican": Console.WriteLine("You voted Republican. "); Console.WriteLine("Don't you feel compassionate?"); break; case "Progressive": Console.WriteLine("You voted Progressive. "); break; default: Console.WriteLine("You did not make a valid choice."); break; } Console.WriteLine("Thank you for voting."); } } }