Section 9.2. The foreach Statement


9.2. The foreach Statement

The foreach looping statement is new to the C family of languages, though it is already well-known to VB programmers. The foreach statement allows you to iterate through all the items in an array or other collection, examining each item in turn. The syntax for the foreach statement is:

foreach (type identifier in expression) statement

Thus, you might update Example 9-1 to replace the for statements that iterate over the contents of the populated array with foreach statements, as shown in Example 9-2.

Example 9-2. Using foreach
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace UsingForEach {       // a simple class to store in the array       public class Employee       {          // a simple class to store in the array          public Employee( int empID )          {             this.empID = empID;          }          public override string ToString( )          {             return empID.ToString( );          }          private int empID;       }       public class Tester       {          static void Main( )          {             int[] intArray;             Employee[] empArray;             intArray = new int[5];             empArray = new Employee[3];             // populate the array             for ( int i = 0; i < empArray.Length; i++ )             {                empArray[i] = new Employee( i + 5 );             }             foreach ( int i in intArray )             {                Console.WriteLine( i.ToString( ) );             }             foreach ( Employee e in empArray )             {                Console.WriteLine( e.ToString( ) );             }          }       }    } }

The output for Example 9-2 is identical to Example 9-1. However, instead of creating a for statement that measures the size of the array and uses a temporary counting variable as an index into the array as in the following, we try another approach:

for (int i = 0; i < empArray.Length; i++) {     Console.WriteLine(empArray[i].ToString()); }

We iterate over the array with the foreach loop, which automatically extracts the next item from within the array and assigns it to the temporary object you've created in the head of the statement:

foreach (Employee e in empArray) {    Console.WriteLine(e.ToString());  }

The object extracted from the array is of the appropriate type; thus, you may call any public method on that object.

9.2.1. Initializing Array Elements

It is possible to initialize the contents of an array at the time it is instantiated by providing a list of values delimited by curly brackets ({}). C# provides a longer and a shorter syntax:

int[] myIntArray = new int[5] { 2, 4, 6, 8, 10 } int[] myIntArray = { 2, 4, 6, 8, 10 }

There is no practical difference between these two statements, and most programmers will use the shorter syntax, but see the following note.

The reason both syntaxes exist is that in some rare circumstances you have to use the longer syntaxspecifically, if the C# compiler is unable to infer the correct type for the array.


9.2.2. The params Keyword

You can create a method that displays any number of integers to the console by passing in an array of integers and then iterating over the array with a foreach loop. The params keyword allows you to pass in a variable number of parameters without necessarily explicitly creating the array.

In the next example, you create a method, DisplayVals(), that takes a variable number of integer arguments:

public void DisplayVals(params int[] intVals)

The method itself can treat the array as if an integer array were explicitly created and passed in as a parameter. You are free to iterate over the array as you would over any other array of integers:

foreach (int i in intVals) {    Console.WriteLine("DisplayVals {0}",i); }

The calling method, however, need not explicitly create an array: it can simply pass in integers, and the compiler will assemble the parameters into an array for the DisplayVals( ) method:

t.DisplayVals(5,6,7,8);

You are free to pass in an array if you prefer:

int [] explicitArray = new int[5] {1,2,3,4,5}; t.DisplayVals(explicitArray);

Example 9-3 provides the complete source code illustrating the params keyword.

Example 9-3. Using the params keyword
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace UsingParams {    public class Tester    {       static void Main( )       {          Tester t = new Tester( );          t.DisplayVals(5,6,7,8);          int [] explicitArray = new int[5] {1,2,3,4,5};          t.DisplayVals(explicitArray);       }       public void DisplayVals(params int[] intVals)       {          foreach (int i in intVals)          {             Console.WriteLine("DisplayVals {0}",i);          }       }    } } Output: DisplayVals 5 DisplayVals 6 DisplayVals 7 DisplayVals 8 DisplayVals 1 DisplayVals 2 DisplayVals 3 DisplayVals 4 DisplayVals 5

9.2.3. Multidimensional Arrays

Arrays can be thought of as long rows of slots into which values can be placed. Once you have a picture of a row of slots, imagine 10 rows, one on top of another. This is the classic two-dimensional array of rows and columns. The rows run across the array and the columns run up and down the array.

A third dimension is possible, but somewhat harder to imagine. Make your arrays three-dimensional, with new rows stacked atop the old two-dimensional array. OK, now imagine four dimensions. Now imagine 10.

Those of you who aren't string-theory physicists have probably given up, as have I. Multidimensional arrays are useful, however, even if you can't quite picture what they would look like.

C# supports two types of multidimensional arrays: rectangular and jagged. In a rectangular array, every row is the same length. A jagged array, however, is an array of arrays, each of which can be a different length.

9.2.3.1 Rectangular arrays

A rectangular array is an array of two (or more) dimensions. In the classic two- dimensional array, the first dimension is the number of rows and the second dimension is the number of columns.

Java programmers take note: rectangular arrays don't exist in Java.


To declare a two-dimensional array, use the following syntax:

type [,] array-name

For example, to declare and instantiate a two-dimensional rectangular array named myRectangularArray that contains two rows and three columns of integers, you would write:

int [,] myRectangularArray = new int[2,3];

Example 9-4 declares, instantiates, initializes, and prints the contents of a two- dimensional array. In this example, a for loop is used to initialize the elements of the array.

Example 9-4. Rectangular array
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace RectangularArray {    public class Tester    {       static void Main( )       {          const int rows = 4;          const int columns = 3;          // declare a 4x3 integer array          int[,] rectangularArray = new int[rows, columns];          // populate the array          for ( int i = 0; i < rows; i++ )          {             for ( int j = 0; j < columns; j++ )             {                rectangularArray[i, j] = i + j;             }          }          // report the contents of the array          for ( int i = 0; i < rows; i++ )          {             for ( int j = 0; j < columns; j++ )             {                Console.WriteLine( "rectangularArray[{0},{1}] = {2}",                   i, j, rectangularArray[i, j] );             }          }       }    } } Output: rectangularArray[0,0] = 0 rectangularArray[0,1] = 1 rectangularArray[0,2] = 2 rectangularArray[1,0] = 1 rectangularArray[1,1] = 2 rectangularArray[1,2] = 3 rectangularArray[2,0] = 2 rectangularArray[2,1] = 3 rectangularArray[2,2] = 4 rectangularArray[3,0] = 3 rectangularArray[3,1] = 4 rectangularArray[3,2] = 5

In this example, you declare a pair of constant values:

const int rows = 4; const int columns = 3;

that are then used to dimension the array:

int[,] rectangularArray = new int[rows, columns];

Notice the syntax. The brackets in the int[,] declaration indicate that the type is an array of integers, and the comma indicates the array has two dimensions (two commas would indicate three dimensions, and so on). The actual instantiation of rectangularArray with new int[rows, columns] sets the size of each dimension. Here the declaration and instantiation have been combined.

The program fills the rectangle with a pair of for loops, iterating through each column in each row. Thus, the first element filled is rectangularArray[0,0], followed by rectangularArray[0,1] and rectangularArray[0,2]. Once this is done, the program moves on to the next rows: rectangularArray[1,0], rectangularArray[1,1], rectangularArray[1,2], and so forth, until all the columns in all the rows are filled.

Just as you can initialize a one-dimensional array using bracketed lists of values, you can initialize a two-dimensional array using similar syntax. Example 9-5 declares a two-dimensional array (rectangularArray), initializes its elements using bracketed lists of values, and then prints out the contents.

Example 9-5. Initializing a multidimensional array
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace InitializingMultiDimensionalArray {    public class Tester    {       static void Main( )       {          const int rows = 4;          const int columns = 3;          // imply a 4x3 array          int[,] rectangularArray =           {              {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11}          };          for ( int i = 0; i < rows; i++ )          {             for ( int j = 0; j < columns; j++ )             {                Console.WriteLine( "rectangularArray[{0},{1}] = {2}",                   i, j, rectangularArray[i, j] );             }          }       }    } } Output: rectangularArrayrectangularArray[0,0] = 0 rectangularArrayrectangularArray[0,1] = 1 rectangularArrayrectangularArray[0,2] = 2 rectangularArrayrectangularArray[1,0] = 3 rectangularArrayrectangularArray[1,1] = 4 rectangularArrayrectangularArray[1,2] = 5 rectangularArrayrectangularArray[2,0] = 6 rectangularArrayrectangularArray[2,1] = 7 rectangularArrayrectangularArray[2,2] = 8 rectangularArrayrectangularArray[3,0] = 9 rectangularArrayrectangularArray[3,1] = 10 rectangularArrayrectangularArray[3,2] = 11

The preceding example is similar to Example 9-4, but this time you imply the exact dimensions of the array by how you initialize it:

int[,] rectangularArrayrectangularArray =  {     {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11} };

Assigning values in four bracketed lists, each consisting of three elements, implies a 4 3 array. Had you written this as:

int[,] rectangularArrayrectangularArray =  {    {0,1,2,3}, {4,5,6,7}, {8,9,10,11}             };

you would instead have implied a 3 4 array.

You can see that the C# compiler understands the implications of your clustering, since it can access the objects with the appropriate offsets, as illustrated in the output.

You might guess that since this is a 12-element array you can just as easily access an element at rectangularArray[0,3] (the fourth element in the first row) as at rectangularArray[1,0] (the first element in the second row) This works in C++, but if you try it in C#, you will run right into an exception:

Exception occurred: System.IndexOutOfRangeException:  Index was outside the bounds of the array. at Programming_CSharp.Tester.Main() in  csharp\programming csharp\listing0703.cs:line 23

C# arrays are smart, and they keep track of their bounds. When you imply a 4 3 array, you must treat it as such.

9.2.3.2 Jagged arrays

A jagged array is an array of arrays. It is called "jagged" because each row need not be the same size as all the others, and thus a graphical representation of the array would not be square.

When you create a jagged array, you declare the number of rows in your array. Each row will hold an array, which can be of any length. These arrays must each be declared. You can then fill in the values for the elements in these "inner" arrays.

In a jagged array, each dimension is a one-dimensional array. To declare a jagged array, use the following syntax, where the number of brackets indicates the number of dimensions of the array:

type [] []...

For example, you would declare a two-dimensional jagged array of integers named myJaggedArray as follows:

int [] [] myJaggedArray;

Access the fifth element of the third array by writing myJaggedArray[2][4].

Example 9-6 creates a jagged array named myJaggedArray, initializes its elements, and then prints their content. To save space, the program takes advantage of the fact that integer array elements are automatically initialized to 0, and it initializes the values of only some of the elements.

Example 9-6. Working with a jagged array
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace JaggedArray {    public class Tester    {       static void Main( )       {          const int rows = 4;          // declare the jagged array as 4 rows high          int[][] jaggedArray = new int[rows][];          // the first row has 5 elements          jaggedArray[0] = new int[5];          // a row with 2 elements          jaggedArray[1] = new int[2];          // a row with 3 elements          jaggedArray[2] = new int[3];          // the last row has 5 elements          jaggedArray[3] = new int[5];          // Fill some (but not all) elements of the rows                     jaggedArray[0][3] = 15;          jaggedArray[1][1] = 12;          jaggedArray[2][1] = 9;          jaggedArray[2][2] = 99;          jaggedArray[3][0] = 10;          jaggedArray[3][1] = 11;          jaggedArray[3][2] = 12;          jaggedArray[3][3] = 13;          jaggedArray[3][4] = 14;          for ( int i = 0; i < 5; i++ )          {             Console.WriteLine( "jaggedArray[0][{0}] = {1}",                i, jaggedArray[0][i] );          }          for ( int i = 0; i < 2; i++ )          {             Console.WriteLine( "jaggedArray[1][{0}] = {1}",                i, jaggedArray[1][i] );          }          for ( int i = 0; i < 3; i++ )          {             Console.WriteLine( "jaggedArray[2][{0}] = {1}",                i, jaggedArray[2][i] );          }          for ( int i = 0; i < 5; i++ )          {             Console.WriteLine( "jaggedArray[3][{0}] = {1}",                i, jaggedArray[3][i] );          }       }    } } Output: jaggedArray[0][0] = 0 jaggedArray[0][1] = 0 jaggedArray[0][2] = 0 jaggedArray[0][3] = 15 jaggedArray[0][4] = 0 jaggedArray[1][0] = 0 jaggedArray[1][1] = 12 jaggedArray[2][0] = 0 jaggedArray[2][1] = 9 jaggedArray[2][2] = 99 jaggedArray[3][0] = 10 jaggedArray[3][1] = 11 jaggedArray[3][2] = 12 jaggedArray[3][3] = 13 jaggedArray[3][4] = 14

In this example, a jagged array is created with four rows:

int[][] jaggedArray = new int[rows][];

Notice that the second dimension is not specified. This is set by creating a new array for each row. Each array can have a different size:

// the first row has 5 elements jaggedArray[0] = new int[5];   // a row with 2 elements jaggedArray[1] = new int[2];  // a row with 3 elements jaggedArray[2] = new int[3]; // the last row has 5 elements jaggedArray[3] = new int[5];

Once an array is specified for each row, you need only populate the various members of each array and then print out their contents to ensure that all went as expected.

Notice that when you access the members of the rectangular array, you put the indexes all within one set of square brackets:

rectangularArrayrectangularArray[i,j]

while with a jagged array you need a pair of brackets:

jaggedArray[3][i]

You can keep this straight by thinking of the first as a single array of more than one dimension and of the jagged array as an array of arrays.

9.2.4. Array Bounds

The Array class can also be created by using the overloaded CreateInstance method. One of the overloads allows you to specify the lower bounds ( starting index) of each dimension in a multidimensional array. This is a fairly obscure capability, not often used.

Briefly, here is how you do it: you call the static method CreateInstance, that returns an Array, and that takes three parameters: an object of type Type (indicating the type of object to hold in the array), an array of integers indicating the length of each dimension in the array, and a second array of integers indicating the lower bound for each dimension. Note that the two arrays of integers must have the same number of elements; that is, you must specify a lower bound for each dimension:

#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace SettingArrayBounds {    public class SettingArrayBounds    {       public static void CreateArrayWithBounds( )       {          // Creates and initializes a multidimensional          // Array of type String.          int[] lengthsArray = new int[2] { 3, 5 };          int[] boundsArray = new int[2] { 2, 3 };          Array multiDimensionalArray = Array.CreateInstance(                  typeof( String ),                  lengthsArray,                  boundsArray );          // Displays the lower bounds and the          // upper bounds of each dimension.          Console.WriteLine( "Bounds:\tLower\tUpper" );          for ( int i = 0; i < multiDimensionalArray.Rank; i++ )             Console.WriteLine(              "{0}:\t{1}\t{2}",              i,              multiDimensionalArray.GetLowerBound( i ),              multiDimensionalArray.GetUpperBound( i ) );       }       static void Main( )       {          SettingArrayBounds.CreateArrayWithBounds( );       }    } }

9.2.5. Array Conversions

Conversion is possible between arrays if their dimensions are equal and if a conversion is possible between the reference element types. An implicit conversion can occur if the elements can be implicitly converted; otherwise an explicit conversion is required.

It is also possible, of course, to convert an array of derived objects to an array of base objects. Example 9-7 illustrates the conversion of an array of user-defined Employee types to an array of objects.

Example 9-7. Converting arrays
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace ConvertingArrays {    // create an object we can    // store in the array    public class Employee    {       // a simple class to store in the array       public Employee( int empID )       {          this.empID = empID;       }       public override string ToString( )       {          return empID.ToString( );       }       private int empID;    }    public class Tester    {       // This method takes an array of objects.       // We'll pass in an array of Employees       // and then an array of strings.       // The conversion is implicit since both Employee       // and string derive (ultimately) from object.       public static void PrintArray( object[] theArray )       {          Console.WriteLine( "Contents of the Array {0}",             theArray.ToString( ) );          // walk through the array and print          // the values.           foreach ( object obj in theArray )          {             Console.WriteLine( "Value: {0}", obj );          }       }       static void Main( )       {          // make an array of Employee objects          Employee[] myEmployeeArray = new Employee[3];          // initialize each Employee's value          for ( int i = 0; i < 3; i++ )          {             myEmployeeArray[i] = new Employee( i + 5 );          }          // display the values          PrintArray( myEmployeeArray );          // create an array of two strings          string[] array =              {                "hello", "world"             };          // print the value of the strings          PrintArray( array );       }    } } Output: Contents of the Array Programming_CSharp.Employee[] Value: 5 Value: 6 Value: 7 Contents of the Array System.String[] Value: hello Value: world

Example 9-7 begins by creating a simple Employee class, as seen earlier in the chapter. The Tester class now contains a new static method, PrintArray( ), that takes as a parameter a one-dimensional array of Objects:

public static void PrintArray(object[] theArray)

Object is the implicit base class of every object in the .NET Framework, and so is the base class of both String and Employee.

The PrintArray() method takes two actions. First, it calls the ToString() method on the array itself:

Console.WriteLine("Contents of the Array {0}",      theArray.ToString());

System.Array overrides the ToString( ) method to your advantage, printing an identifying name of the array:

Contents of the Array Programming_CSharp. Employee [] Contents of the Array System.String[]

PrintArray( ) then goes on to call ToString() on each element in the array it receives as a parameter. Because ToString( ) is a virtual method in the base class Object, it is guaranteed to be available in every derived class. You have overridden this method appropriately in Employee so that the code works properly. Calling ToString( ) on a String object might not be necessary, but it is harmless and it allows you to treat these objects polymorphically.

9.2.6. Sorting Arrays

Two useful static methods of Array are Sort() and Reverse(). These are fully supported for arrays of the built-in C# types such as string. Making them work with your own classes is a bit trickier, as you must implement the IComparable interface (see the section "Implementing IComparable" later in this chapter). Example 9-8 demonstrates the use of these two methods to manipulate String objects.

Example 9-8. Using Array.Sort and Array.Reverse
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace ArraySortAndReverse {    public class Tester    {       public static void PrintMyArray( object[] theArray )       {          foreach ( object obj in theArray )          {             Console.WriteLine( "Value: {0}", obj );          }          Console.WriteLine( "\n" );       }       static void Main( )       {          String[] myArray =              {                 "Who", "is", "John", "Galt"              };          PrintMyArray( myArray );          Array.Reverse( myArray );          PrintMyArray( myArray );          String[] myOtherArray =              {                 "We", "Hold", "These", "Truths",                 "To", "Be", "Self","Evident",              };          PrintMyArray( myOtherArray );          Array.Sort( myOtherArray );          PrintMyArray( myOtherArray );       }    } } Output: Value: Who Value: is Value: John Value: Galt Value: Galt Value: John Value: is Value: Who Value: We Value: Hold Value: These Value: Truths Value: To Value: Be Value: Self Value: Evident Value: Be Value: Evident Value: Hold Value: Self Value: These Value: To Value: Truths Value: We

The example begins by creating myArray, an array of strings with the words:

"Who", "is", "John", "Galt"

This array is printed, and then passed to the Array.Reverse( ) method, where it is printed again to see that the array itself has been reversed:

Value: Galt Value: John Value: is Value: Who

Similarly, the example creates a second array, myOtherArray, containing the words:

"We", "Hold", "These", "Truths", "To", "Be", "Self", "Evident",

This is passed to the Array.Sort( ) method. Then Array.Sort() happily sorts them alphabetically:

Value: Be Value: Evident Value: Hold Value: Self Value: These Value: To Value: Truths Value: We



Programming C#(c) Building. NET Applications with C#
Programming C#: Building .NET Applications with C#
ISBN: 0596006993
EAN: 2147483647
Year: 2003
Pages: 180
Authors: Jesse Liberty

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net