Building Loops Using Iteration Statements

Building Loops Using Iteration Statements

The next set of statements we’ll look at are the iteration statements, which are used to build loops in C#. Loops enable you to execute one or more statements multiple times. You can specify that your loop is to be executed a specific number of times, or you can iterate until a specified condition occurs. C# offers four iteration statements, each of which is well-suited for a specific kind of loop.

The for Loop

The for loop is used to execute a loop until a specified condition becomes true, as follows:

for(initialization; condition; iteration)
    controlled-statement;

One of the benefits of using the for loop is that all the conditions for initializing and maintaining the loop are located in one place, at the top of the loop.

The for loop has four components: the initialization expression, the conditional expression, the controlled statement, and the iteration expression. These components are executed in the following order:

  1. The initialization expression is run once when the for loop begins execution.

  2. The conditional expression is evaluated to determine whether the for loop should continue executing; if the expression has a value of false, control passes to the statements after the for loop.

  3. The controlled statement is executed if the conditional expression evaluates to true.

  4. The iteration expression is executed once after each execution of the controlled statement.

The algorithm for the for loop is shown in Figure 5-1.

Figure 5-1.
Flowchart illustrating the operation of the for loop.

In general, the for loop is used when a loop is executed a specific number of times, such as iterating over all of the elements in an array, as shown here:

int [] intArray = {1, 2, 3};
for(int i = 0; i < intArray.Length; ++i)
{
    Console.WriteLine(intArray[i]);
} 

This for loop prints each element of intArray, as shown here:

1
2
3

Each of the four expressions that constitute a for loop can be replaced by an empty expression. An empty conditional expression is considered to be true, which means that any for loop can safely omit any expressions that aren’t needed. The minimal for loop—an infinite loop with no terminating condition—looks like this:

for(;;);

A variable declared in the initialization portion of the for loop remains visible and in scope for the lifetime of the for loop and isn’t accessible after the end of the loop. This arrangement allows you to reuse variable names in successive loops. In the following example, two for loops initialize an int named i, which is used as an index for array indexing:

int [] intArray = {1, 2, 3};
for(int i = 0; i < intArray.Length; ++i)
{
    Console.WriteLine(intArray[i]);
}
int [] ageArray = {5, 8, 39, 40};
for(int i = 0; i < ageArray.Length; ++i)
{
    Console.WriteLine(ageArray[i]);
}

In keeping with the C# restriction against redeclaring variable names in nested blocks, any variables declared in the initialization expression must have unique names that don’t conflict with names in the outer block, as shown here:

int count = 42;
string [] names = new string[] {"Ali", "Kenzie"};
// Won't compile; count isn't a unique name.
for(count = 0; count < names.Length; ++count)
{
    
    

}

The following example shows a typical use of a for loop, iterating over the contents of an array of Shakespeare quotations and displaying each quotation using Console.WriteLine:

class QuoteArrayApp
{
    static string [] quotes = new string []
    {
        // Henry V
        "We few, we happy few, we band of brothers.",
        // Hamlet
        "Alas, poor Yorick!",
        // Titus Andronicus
        "I know them all, though they suppose me mad.",
        // Twelfth Night
        "Some are born great, some achieve greatness, " +
        "and others have greatness thrust upon them."
    };
    static void Main(string[] args)
    {
        for(int i = 0; i < quotes.Length; ++i)
        {
            Console.WriteLine(quotes[i]);
        }
    }
}
The foreach Loop

As you saw in the previous section, a common use of the for loop is to iterate over items in an array. C# includes another looping statement, foreach, that’s optimized for iterating over every item in a collection such as an array. The foreach statement is similar to the For Each loop long used by Visual Basic programmers to simplify the building of loops.

The algorithm for the foreach loop is shown in Figure 5-2.

Figure 5-2.
Flowchart illustrating the foreach loop, which is used to iterate over each member of a collection.

As shown in Figure 5-2, the C# foreach statement allows you to define a variable that’s updated for each iteration of the loop, as follows:

foreach(type local-variable in collection)
    statement;

Each time the controlled statement or statement block for a foreach loop is executed, the locally declared variable is updated to refer to the next member in the collection, simplifying the code in the loop body, as shown here:

int [] rg = {1, 2, 3, 4};
foreach(int i in rg)
{
    Console.WriteLine(i);
}

This foreach loop generates the following output:

1
2
3
4

The foreach loop isn’t intended to be used to change the value of items stored in a collection. The compiler will enforce this condition when possible. For example, when primitive types are stored in an array, you can’t change the value of array elements via the local variable:

int [] rg = {1, 2, 3, 4, 5, 6, 7};
foreach(int i in rg)
{
    // Not allowed; array elements are read-only.
    i = 0;
}

As you can see, it’s not possible to overwrite or clear elements in a collection using a foreach loop. However, when nonprimitive types are stored in a collection, it’s possible to call methods that update the state of individual objects.

The following example loops over the command-line arguments passed to the application’s Main method:

class AverageApp
{
    static void Main(string[] args)
    {
        decimal total = 0;
        foreach(string arg in args)
        {
            total += Convert.ToDecimal(arg);
        }
        if(args.Length > 0)
        {
            decimal average = total / args.Length;
            Console.WriteLine("Average is {0}", average);
        }
    }
}

Command-line arguments for C# applications are passed as an array of strings, with the first argument passed as the first array element, the second argument passed as the second array element, and so on. Unlike the standard behavior in C and C++ applications, the name of the application isn’t passed as the first argument; the array contains only parameters passed on the command line.

In this example, a foreach loop is used to visit each element in the array of command-line arguments and the Convert.ToDecimal method is used to convert each of the arguments to a decimal value. After the decimal values for all command-line parameters are combined, the average is calculated and displayed by calling Console.WriteLine.

The foreach loop can be used with any of the built-in collection types in the .NET Framework. If you create your own collection classes, you must implement specific methods that will be used to iterate over your collection. (An example demonstrating the implementation of a foreach-friendly collection will be presented in Chapter 8.) The use of these iteration methods impacts performance slightly, and you can often write a faster loop by using a for loop. However, the C# compiler detects when foreach is used with an array and performs code optimizations to bring the performance of the foreach statement more in line with the performance of a for loop.

The while Loop

The while statement is used to create a loop that executes a statement or block of statements repeatedly for as long as a supplied expression evaluates as true, as follows:

while(condition)
    statement;

Because the condition is tested before the loop statement is executed, a while loop will execute zero or more times, as illustrated in Figure 5-3.

Figure 5-3.
Flowchart illustrating the while loop, which is used to execute a statement while an expression remains true.

The while statement is well-suited for loops in which the number of iterations isn’t known in advance. For example, when you create code to read an Extensible Markup Language (XML) stream using the XmlTextReader class, you’ll often use the while loop to read all the data in the stream, as follows:

XmlTextReader reader = new XmlTextReader("books.xml");
while(reader.Read())
{
    switch(reader.NodeType)
    {
        
    

    }
}

To create a loop that executes until it’s explicitly exited, use true as the conditional expression, as shown here:

while(true)
{
    
    

}

As with the other iteration statements, it’s important to ensure a terminating condition for a while loop, either in the conditional statement or by adding a jump statement such as a break statement to the body of the loop, as will be discussed later in this chapter, in the section “Using Jump Statements to Transfer Control.”

The do Loop

The do statement is similar to the while statement, but the conditional test is performed after each iteration of the do loop, as shown here:

do
    statement;
while(condition);

Unlike the while loop, the controlled statements associated with a do loop must execute at least once, as shown in Figure 5-4.

Figure 5-4.
Flowchart illustrating the do loop, which executes at least once, even if the conditional expression evaluates to false.

Loops that require at least one pass are sometimes written using a while statement, which leads to duplicate code. An example of such a code fragment is shown here:

Console.WriteLine("Enter your name:");
string name = Console.ReadLine();
while(name.Length > 0)
{
    AddNameToDatabase(name);
    Console.WriteLine("Enter your name:");
    name = Console.ReadLine();
}

In this example, the calls to Console.WriteLine and Console.ReadLine are duplicated, resulting in code that’s difficult to maintain. This code could be rewritten to use a do statement, placing all of the controlled statements within the loop body, like this:

string name;
do {
    Console.WriteLine("Enter your name:");
    name = Console.ReadLine();
    if(name.Length > 0)
        AddNameToDatabase(name);
} while(name.Length > 0);

The second version is easier to maintain because there are no controlled statements that execute outside the loop body. The loop could be simplified still further by providing an explicit break statement inside the loop and replacing the conditional expression with true, as follows:

string name;
do {
    Console.WriteLine("Enter your name:");
    name = Console.ReadLine();
    if(name.Length == 0)
        break;
    AddNameToDatabase(name);
} while(true);

The following program recursively lists the contents of a directory, beginning at a path specified by the user. At the heart of this example is a do statement that requests a path from the user and then calls DisplayDirectoryInfo to display information about the directory.

class DirListApp
{
    static void Main(string[] args)
    {
        string directoryPath;
        do {
            Console.WriteLine("Enter path, or <return> to quit.");
            directoryPath = Console.ReadLine();
            if(directoryPath.Length == 0)
                break;
            // Get a DirectoryInformation array for the
            // specified path.
            DirectoryInfo info = new DirectoryInfo(directoryPath);
            DisplayDirectoryInfo(info);
        } while(true);
    }
    static void DisplayDirectoryInfo(DirectoryInfo info)
    {
        try
        {
            DirectoryInfo[] directories = info.GetDirectories();
            foreach(DirectoryInfo directory in directories)
            {
                DisplayDirectoryInfo(directory);
            }
            FileInfo[] files = info.GetFiles();
            foreach(FileInfo file in files)
            {
                Console.WriteLine(file);
            }
        }
        catch(DirectoryNotFoundException exc)
        {
            Console.WriteLine("Could not find the directory.");
        }
        catch(Exception exc)
        {
            Console.WriteLine(exc);
        }
    }
}

In this example, the program begins by requesting a directory path from the user. As long as the user continues to supply directory paths, the program will continue to execute its main loop. Because at least one iteration is always required, the do statement is a good choice for this sort of loop.



Part III: Programming Windows Forms