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.”
C# provides a number of constructs that allow you to write conditional branches into your programs; these constructs are described in the following sections.
The 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
using System; namespace Branching { class Test { 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 ); } // end if } // end Main } // end class } // end namespace
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 sidebar, "Brace Styles,” later 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), 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
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. Single statements with if
using System; namespace Branching { class Test { 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 ); } // end Main } // end class } // end namespace
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 arise.
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
using System; namespace Branching { class Test { 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!" ); } // end Main } // end class } // end namespace
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.
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. 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; }
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 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
using System; namespace Branching { class Test { 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 ); } // end if else { Console.WriteLine( "Nope, ValueOne: {0} is NOT larger than ValueTwo: {1}", valueOne, valueTwo ); } // end else } // end Main } // end class } // end namespace
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.
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:
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 ice patches.
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. 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 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
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 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.
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 “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
using System; class Values { enum Party { Democrat, Republican, Progressive } static void Main() { // hard wire 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." ); } }
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
using System; class Values { enum Party { Democrat, Republican, Progressive } static void Main() { // hard wire 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
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).
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 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.
The goto
keyword is an
unconditional branch. When the compiler sees this word, it
immediately transfers the flow (jumps) to wherever the goto
points to. 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, 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:
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. Switching on a string
using System; class Values { 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." ); } }