What’s in This Chapter
if-else
and switch
statements?:
and ??
operatorsfor
, while
, do
, and foreach
loopsbreak
and continue
statementsWrox.com Downloads for This Chapter
Please note that all the code examples for this chapter are available as a part of this chapter’s code download on the book’s website at www.wrox.com/go/csharp5programmersref on the Download Code tab.
Program control statements tell an application which other statements to execute under different circumstances. They control the path that execution takes through the code. They include commands that tell the program to execute some statements but not others and to execute certain statements repeatedly.
The two main categories of control statements are decision statements (or conditional statements) and looping statements. The following sections describe in detail the decision and looping statements provided by C#.
A decisions statement or conditional statement represents a branch in the program. It marks a place where the program can execute one set of statements or another, or possibly no statements at all, depending on some condition. These include several kinds of if
statements and switch
statements.
The if-else
statement has the following syntax:
if (condition1) statement1;
else if (condition2) statement2;
else if (condition3) statement3;
...
else statementElse;
The conditions are logical expressions that evaluate to either true
or false
. The statements are the statements that should be executed if the corresponding condition is true
.
The program evaluates each of the conditions until it finds one that is true
. It then evaluates the corresponding statement and skips all the rest of the series of else
and if
statements.
If none of the conditions are true
, the program executes statementElse.
For example, consider the following code snippet.
string greeting = "";
if (DateTime.Now.DayOfWeek == DayOfWeek.Monday)
greeting = "Sorry, it's Monday.";
else if (DateTime.Now.DayOfWeek == DayOfWeek.Friday)
greeting = "Finally, it's Friday!";
else
greeting = "Welcome to " + DateTime.Now.DayOfWeek.ToString();
MessageBox.Show(greeting);
The code starts by creating the string
variable greeting
and initializing it to a blank string. It then begins a series of if-else
statements.
If it’s Monday, the program sets greeting
to "Sorry, it's Monday."
If it’s not Monday and it is Friday, the program sets greeting
to "Finally, it's Friday!"
If it’s neither Monday nor Friday, the program sets greeting
to "Welcome to"
followed by the current day of the week.
After the series of if-else
statements, the program displays the greeting in a message box.
The final else
section is optional. If you don’t include it and none of the conditions are true
, the program doesn’t execute any of the corresponding statements.
All the else if
statements are also optional. If the code includes only an if
statement, either the program executes the statement that follows or it doesn’t, depending on the value of the condition.
For example, the following code uses a single if
statement to display a message box only if it’s Monday. If it’s any other day of the week, the program does nothing.
if (DateTime.Now.DayOfWeek == DayOfWeek.Monday)
MessageBox.Show("Sorry, it's Monday.");
The “statement” that the program executes when a condition is true
can actually be a block of statements surrounded by braces. The following shows the syntax.
if (condition1)
{
statement1;
}
else if (condition2)
{
statement2;
}
else if (condition3)
{
statement3;
}
...
else
{
statementElse;
}
You can place as many statements as you like between the braces.
The switch
statement lets a program execute one of several pieces of code depending on a single value. The result is similar to a series of if-else
statements that compare a single value to a series of other values.
The basic syntax is as follows:
switch (value)
{
case expression1:
statements1;
break;
case expression2:
statements2;
break;
...
«default:
statementsDefault
break;»
}
Here the program compares value to the expressions until it finds one that matches or it runs out of expressions to test. The expressions must be constant statements and cannot duplicate each other.
If the program finds a match, it executes the corresponding code. If the program runs out of expressions, it executes the statements in the default
section (if it is present).
You can place multiple case
statements in a group to make them all execute the same code. However, you cannot allow the code from one case section to fall through to the next. If the first section contains lines of code, it must end with a break
statement before the next case begins.
The following code is similar to the previous code that checks the day of the week except this version adds extra code for Saturday and Sunday.
string greeting = "";
switch (DateTime.Now.DayOfWeek)
{
case DayOfWeek.Monday:
greeting = "Sorry, it's Monday.";
break;
case DayOfWeek.Friday:
greeting = "Finally, it's Friday!";
break;
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
greeting = "Yay! The weekend!";
break;
default:
greeting = "Welcome to " + DateTime.Now.DayOfWeek.ToString();
break;
}
MessageBox.Show(greeting);
This code initializes the greeting variable and then executes a switch
statement. Depending on the values of DateTime.Now.DayOfWeek
, the program executes the code in the appropriate case
section.
The values DayOfWeek.Monday
and DayOfWeek.Friday
have their own sections. The two values DayOfWeek.Saturday
and DayOfWeek.Sunday
both execute code that sets greeting
to "Yay! The weekend!"
If DateTime.Now.DayOfWeek
is not any of those values, the default
section sets greeting
equal to "Welcome to"
followed by the day of the week.
The switch
statement works naturally with lists of discrete values because each case
statement can handle one value.
Enumerated types also work with discrete values, so they work well with switch
statements. The enumerated type defines the values and the switch
statement uses them, as shown in the following code fragment.
private enum JobStates
{
Pending,
Assigned,
InProgress,
ReadyToTest,
Tested,
Released,
}
private JobStates JobState;
...
switch (JobState)
{
case JobStates.Pending:
...
break;
case JobStates.Assigned:
...
break;
case JobStates.InProgress:
...
break;
case JobStates.ReadyToTest:
...
break;
case JobStates.Tested:
...
break;
case JobStates.Released:
...
break;
}
To catch bugs when changing an enumerated type, many developers include a default
section that throws an exception. If you later add a new value to the enumerated type but forget to add corresponding code to the switch
statement, the default
section throws an exception, so you can fix the code.
For more information on enumerated types, see the section “Enumerations” in Chapter 4.
The conditional or ternary operator ?:
evaluates a boolean expression and returns one of two values, depending on whether the expression is true
or false
. For example, the following code examines an Employee
object’s IsManager
property. If IsManager
is true
, the code sets the employee’s Salary
to 90,000
. If IsManager
is false
, the code sets the employee’s Salary
to 10,000
.
emp.Salary = emp.IsManager ? 90000 : 10000;
This is equivalent to the following if-else
statement.
if (emp.IsManager) emp.Salary = 90000;
else emp.Salary = 10000;
The null-coalescing operator ??
examines a reference value. It returns that value if it is not null
. It returns its second argument if the first value is null
. For example, the following code sets variable orderedBy
equal to customer
if customer
is not null
. If customer is null
, the code sets orderedBy
equal to a new Customer
object.
Customer orderedBy = customer ?? new Customer();
This is equivalent to the following if-else
statement.
Customer orderedBy;
if (customer != null) orderedBy = customer;
else orderedBy = new Customer();
For more information on the conditional and null-coalescing operators, see the section “Conditional and Null-coalescing Operators” in Chapter 5.
Looping statements make the program execute a series of statements repeatedly. C# provides four kinds of loops: for
loops, while
loops, do
loops, and foreach
loops.
A for
loop has the following syntax.
for («initialization»; «test»; «increment») statement;
The parts of the loop are as follows:
true
, the loop continues. If the result is false
, the loop ends. (Note that this means the loop might not execute even once if the condition is false
when the loop starts.)true
. You can make the loop include multiple statements by enclosing them in braces.The following code shows a simple for
loop.
for (int i = 1; i <= 10; i++)
{
Console.WriteLine(i);
}
The initialization part of the for
statement declares integer variable i
and sets it equal to 1
. The test part of the loop determines whether i
is less than or equal to 10
. The increment part of the loop increases i
by 1. Taken together, these pieces of the for
statement make the loop run for values i
= 1, i
= 2, i
= 3, ..., i
= 10. For each value of i
, the program executes the Console.WriteLine
statement to display the value of i
.
Usually the loop’s initialization piece declares and initializes a single value as in the previous code. If the looping variable has already been declared, you can leave this section blank or only initialize the variable. For example, the following code declares the looping variable i
outside of the loop.
int i;
for (i = 1; i < 10; i++)
{
Console.WriteLine(i);
}
The initialization section can also declare and initialize multiple variables of the same type, and the increment section can execute multiple commands separated by commas. For example, the following code shows a for
loop that uses three looping variables: a
, b
, and c
.
for (int a = 0, b = 1, c = 1; a < 1000; a = b, b = c, c = a + b)
{
Console.WriteLine("a: " + a);
}
In this example, the initialization code declares and initializes the three looping variables, the test
section makes the loop run as long as a
< 1000, and the increment
section updates all three variables.
If you omit the test entirely, the loop runs indefinitely. In that case the loop’s code usually contains a return
or other statement to break out of the loop. For example, the following code enters an infinite loop. Each time through the loop, it does something and then asks the user whether it should continue. If the user clicks No, the loop’s return
statement makes the program exit the method that contains the loop.
for (; ; )
{
// Do something ...
if (MessageBox.Show("Continue?", "Continue?", MessageBoxButtons.YesNo)
== DialogResult.No) return;
}
Usually a for
loop’s control variable has an integral data type such as a int
or long
. Because you can do all sorts of strange things inside a for
loop’s initialization and increment sections, you aren’t restricted to integer looping variables. For example, the following code uses a float
variable to display the values 1.0, 1.5, 2.0, 2.5, and 3.0.
for (float x = 1f; x <= 3f; x += 0.5f)
Console.WriteLine(x.ToString("0.0"));
Because floating-point numbers cannot exactly represent every possible value, these data types are subject to rounding errors that can lead to unexpected results in for
loops. The preceding code works as you would expect, at least on my computer. The following code, however, has problems. Ideally, this code would display values between 1 and 2, incrementing them by 1/7. Because of rounding errors, however, the value of x
after seven trips through the loop is approximately 1.85714316. The program adds 1/7 to this and gets 2.00000024. This is greater than the stopping value 2, so the program exits the loop and the Console.WriteLine
statement does not execute for x
= 2.
for (float x = 1; x <= 2; x += 1f / 7f)
{
Console.WriteLine(x);
}
One solution to this type of problem is to convert the loop into one that uses an integer control variable. Integer variables don’t have the same problems with rounding errors that floating-point numbers have, so you get more precise control over the values used in the loop.
The following code does roughly the same thing as the previous code. However, this version uses an integer control variable, so this loop executes exactly eight times as desired. The final value printed into the Console window by the program is 2.
float x = 1;
for (int i = 1; i <= 8; i++, x += 1f / 7f)
{
Console.WriteLine(x);
}
A while
loop has the following syntax.
while (test)
statement;
As long as the test evaluates to true
, the loop executes its statement. Note that this means the loop might not execute even once if the test is false
when the loop starts.
You cannot omit the test but you can set it to true
to make the while
loop repeat indefinitely. If you want to execute more than one statement inside the loop, enclose them in braces. Usually the code inside the loop makes something change so the test eventually evaluates to false
and stops the loop.
For a simple example of a while
loop, suppose values is an array of integers. The following code uses a while
loop to search the array for the value 37.
int index = 0;
while ((index < values.Length) && (values[index] != 37))
index++;
The code initializes the variable index
to 0. Then as long as index
is a valid index in the array and the values[index]
entry isn’t 37, the loop increments index
. (After the loop finishes, if index
is equal to the length of the array, the value wasn’t found.)
A do
loop has the following syntax.
do
statement;
while (test);
A do
loop is similar to a while
loop except it performs its test after the loop has executed instead of before it executes. That means a do
loop always executes at least once.
The following code shows a do
loop version of the previous example that searches an array for the value 37.
int index = -1;
do
index++;
while ((index < values.Length) && (values[index] != 37));
As is the case for a while
loop, if you want to execute multiple statements in a do
loop, you should enclose them in braces.
A foreach
loop iterates over the items in a collection, array, or other container class that supports foreach
loops. A foreach
loop has the following syntax.
foreach (variable in group)
statement;
Here, group is a collection, array, or other object that supports foreach
. As in for
loops, the looping variable must be declared either in or before the foreach
statement.
The following code shows a simple foreach
loop.
foreach (Employee employee in employees)
{
Console.WriteLine(employee.Name);
}
Each time through the loop, the program sets the variable employee
equal to the next item in the employees
array. The code inside the loop displays the employee
object’s Name
in the Console window.
The looping variable must be of a data type compatible with the objects contained in the group. If the group contains Employee
objects, the variable could be an Employee
object. It could also be a generic object
or any other class that readily converts into an Employee
object. For example, if Employee
inherits from the Person
class, the variable could be of type Person
.
C# doesn’t automatically understand what kinds of objects are stored in a collection or array until it tries to use them. If the looping variable’s type is not compatible with a value in the collection, the program throws an InvalidCastException
when it tries to assign the looping variable to that value.
That means if a collection or array contains more than one type of object, the looping variable must be of a type that can be converted into all the objects’ types. If the objects in a collection do not inherit from a common ancestor class, the code must use a control variable of type object
.
Your code can change the value of the looping variable inside the loop, but that has no effect on the loop’s progress through the collection or array. The loop resets the variable to the next object and continues as if you had never changed the variable’s value. To avoid confusion, don’t bother.
The program cannot modify the collection itself while the loop is executing. For example, if the code is looping over a list of Employee
objects, the code inside the loop cannot add or remove objects from the list. (That could be confusing, so this probably wouldn’t be a good idea even if it were allowed.)
One common scenario when dealing with collections is examining every item in the collection and removing some of them. A foreach
loop won’t let you remove items while you’re looping over them so that’s not an option.
One approach that seems like it might work (but doesn’t) is to use a for
loop, as shown in the following code.
for (int i = 0; i < employees.Count; i++)
{
if (employees[i].ShouldBeRemoved) employees.RemoveAt(i);
}
Unfortunately, if the code removes an object from the collection, the loop skips the next item because its index has been reduced by one and the loop has already passed that position in the collection.
One solution to this problem is to use a for
loop to examine the collection’s objects in reverse order, as shown in the following example.
for (int i = employees.Count - 1; i >= 0; i--)
{
if (employees[i].ShouldBeRemoved) employees.RemoveAt(i);
}
In this version, the code never needs to use an index after it has been removed because the code is counting backward. The index of an object in the collection also doesn’t change unless that object has already been examined by the loop. The loop examines every item exactly once, no matter which objects are removed.
An enumerator is an object that lets you move through the objects contained by some sort of container class. For example, collections, arrays, and hash tables provide enumerators. This section discusses enumerators for collections, but the same ideas apply for these other classes.
You can use an enumerator to view the objects in a collection but not to modify the collection itself. You can use the enumerator to alter the objects in the collection (for example, to change their properties), but you can generally not use it to add, remove, or rearrange the objects in the collection.
Initially, an enumerator is positioned before the first item in the collection. Your code should use the enumerator’s MoveNext
method to step to the next object in the collection. MoveNext
returns true
if it successfully moves to a new object or false
if there are no more objects in the collection.
The Reset
method restores the enumerator to its original position before the first object, so you can step through the collection again.
The Current
method returns the object that the enumerator is currently reading. Invoking Current
throws an exception if the enumerator is not currently reading any object. That happens if the enumerator is before the first object or after the last object.
The following example uses an enumerator to loop through the items in a list named employees
.
IEnumerator<Employee> enumerator = employees.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current.Name);
}
This code creates an enumerator to enumerate over Employee
objects. It then enters a while
loop. If enumerator.MoveNext
returns true
, the enumerator has successfully moved to the next object in the collection. As long as it has read an object, the program uses enumerator.Current
to get the current object and displays that object’s Name
property in the Console window.
When it reaches the end of the employees
list, the enumerator’s MoveNext
method returns false
and the loop ends.
A foreach
loop provides roughly the same access to the items in a container class as an enumerator. Under some circumstances, however, an enumerator may provide a more natural way to loop through a container class than a foreach
loop. For example, an enumerator can skip several items without examining them closely. You can also use an enumerator’s Reset
method to restart the enumeration. To restart a foreach
loop, you would need to start the loop over.
The IEnumerable
interface defines the features needed for enumerators, so any class that implements the IEnumerable
interface provides enumerators. Any class that supports foreach
must also implement the IEnumerable
interface, so any class that supports foreach
also supports enumerators. A few of the classes that implement IEnumerable
include the following:
Array | HybridDictionary | SqlDataReader |
ArrayList | ListDictionary | Stack |
Collection | MessageQueue | String |
CollectionBase | OdbcDataReader | StringCollection |
ControlCollection | OleDbDataReader | StringDictionary |
DataView | OracleDataReader | TableCellCollection |
DictionaryBase | Queue | TableRowCollection |
DictionaryEntries | ReadOnlyCollectionBase | XmlNode |
Hashtable | SortedList | XmlNodeList |
An iterator is similar in concept to an enumerator. It also provides methods that enable you to step through the items in some sort of container object. Iterators are more specialized than enumerators and work with particular classes.
For example, a GraphicsPath
object represents a series of connected lines and curves. A GraphicsPathIterator
object can step through the line and curve data contained in a GraphicsPath
object.
Iterators are much more specialized than enumerators. How you use them depends on what you need to do and on the kind of iterator, so they are not described in detail here.
Each type of loop has a normal termination condition. For example, a while
loop continues to execute as long as the test in the while
statement is true
. When the test becomes false
, the loop ends.
In addition to a loop’s standard method for ending, there are a few other ways a program can leave a loop.
One way to break out of a loop early is to execute a return
statement. When the program encounters a return
statement, the method immediately exits, even if that means breaking out of one or more loops.
The break
statement immediately stops the innermost loop containing the statement and passes control to the end of the loop. For example, suppose the Machine
class represents the machines in a workshop. The following code searches the array machines
for a machine
that isn’t busy.
Machine idleMachine = null;
foreach (Machine machine in machines)
{
if (machine.IsIdle)
{
idleMachine = machine;
break;
}
}
if (idleMachine != null)
{
// Assign work to the machine.
}
First, the program declares the variable idleMachine
and initializes it to null
. It then loops through the Machines
array. If it finds an object in the array with IsIdle
property true
, it saves that machine in the variable idleMachine
and uses the break
statement to end the loop.
After the loop finishes, the code checks idleMachine
to see if it found an idle machine.
A loop can also end early if it encounters an exception and doesn’t include code to handle it. In that case, the loop ends and control passes to an enclosing try catch finally
block if there is one. If the loop isn’t enclosed in a try catch finally
block, control leaves the executing method and passes up the call stack to the method that called it. If there is an active try catch finally
block at that level, it handles the exception. If there is still no try catch finally
block, control continues to pass up the call stack until the exception is handled or control pops off the stack, in which case the program crashes. (For more information about try catch finally
blocks, see Chapter 9, “Error Handling.”)
The continue
statement lets a program skip the rest of its current pass through a loop and start the next pass early.
For example, suppose you need to process a collection of Employee
objects. If an employee is not exempt, you need to perform some overtime calculations for that employee. The following code shows how a program could use the continue
statement to do this.
foreach (Employee employee in employees)
{
if (employee.IsExempt) continue;
// Process the employee.
...
}
Inside the loop, the code checks the Employee
object’s IsExempt
property. If IsExempt
is true
, the program uses the continue
statement to skip the rest of the loop and process the next Employee
.
Control statements form the heart of any program. Decision statements determine what commands are executed, and looping statements determine how many times they are executed.
The if
and switch
statements are the most commonly used decision statements in C#. The ?:
and ??
are less common operators that act as decision statements.
C# provides the for
, while
, do
, and foreach
looping statements. Some container classes also support enumerators that let you step through the items in the container. An enumerator provides more flexibility and can sometimes be more natural than a foreach
loop.
Using the looping statements described in this chapter, you can perform complex searches over collections of objects. For example, you can loop through an array of numbers to find their maximum, minimum, and average values. The next chapter explains how you can use database-like query syntax to perform similar operations more easily. It tells how you can use LINQ to select, filter, and arrange data taken from lists, collections, arrays, and other data structures.
person
is a variable of type Person
. The Person
class has a Type
property of the enumeration type PersonType
. The enumeration includes the values Customer
, Employee
, and Manager
. How are the following two blocks of code different?
// Block 1.
if (person.Type == PersonType.Customer)
{
...
}
else if (person.Type == PersonType.Employee)
{
...
}
else if (person.Type == PersonType.Manager)
{
...
}
// Block 2.
if (person.Type == PersonType.Customer)
{
...
}
if (person.Type == PersonType.Employee)
{
...
}
if (person.Type == PersonType.Manager)
{
...
}
switch
statement instead of if
statements.Person
class has a GetBirthMonth
method that looks a person up in a database and returns the person’s birth month as a number between 1 and 12. Your program should use that method to determine the person’s birthstone. Should you use a series of if
statements or a switch
statement? Why?if
statements and a switch
statement with roughly the same performance to find birthstones as described in Exercise 3.switch
statement or a series of if
statements? Why? Write the code using your chosen approach.for (int a = 0, b = 1, c = 1; a < 1000; a = b, b = c, c = a + b)
{
Console.WriteLine("a: " + a);
}
This code works but it’s rather confusing because its initialization and increment sections contain so many statements. Rewrite this loop to move the initialization out of the loop and the increment statements inside the loop.
for
loop that adds up the numbers in the array values
.while
loop that adds up the numbers in the array values
.do
loop that adds up the numbers in the array values
.for
loop that uses a char
looping variable to display the letters A through Z.Bill
object’s Penalty
property to $5 or 10 percent of the outstanding balance, whichever is greater.
bill.Penalty = bill.Status == BillStatus.Overdue ?
bill.Balance < 50m ? 5m : bill.Balance * 0.1m : 0m;
Rewrite this code so that it doesn’t use the ?:
operator.
??
operator.
Student student = GetStudent("Steward Dent") ?? new Student("Steward Dent");
for
loop that displays the multiples of 3 between 0 and 100 in largest-to-smallest order.for (int i = 1; i < 100; i += i) Console.WriteLine(i);
foreach
loop so that it doesn’t use the continue
statement.
foreach (Employee employee in employees)
{
if (employee.IsExempt) continue;
// Process the employee.
...
}
What advantage does the continue
statement have over your solution? When would it be even more useful?
3.16.207.206