[ Team LiB ] |
9.2 The foreach StatementThe 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 array with foreach statements, as shown in Example 9-2. Example 9-2. Using foreachnamespace Programming_CSharp { using System; // 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, rather than 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 ElementsIt 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 because we are, by nature, lazy. We are so lazy, we'll work all day to save a few minutes doing a task—which isn't so crazy if we're going to do that task hundreds of times! 9.2.2 The params KeywordYou 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 keywordnamespace Programming_CSharp { using System; 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 ArraysArrays 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. Okay, now imagine four dimensions. Now imagine 10. Those of you who are not 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 arraysA 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.
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 arraysnamespace Programming_CSharp { using System; 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 arraynamespace Programming_CSharp { using System; 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 very 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 x 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 x 4 array. You can see that the C# compiler understands the implications of your clustering, since it is able to access the objects with the appropriate offsets, as illustrated in the output. You might guess that this is a 12-element array, and that you can just as easily access an element at rectangularArray[0,3] as at rectangularArray[1,0], but if you try, 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 x 3 array, you must treat it as such. 9.2.3.2 Jagged arraysA jagged array is an array of arrays. It is called "jagged" because each of the rows 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 zero, and it initializes the values of only some of the elements. Example 9-6. Working with a jagged arraynamespace Programming_CSharp { using System; 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 of these arrays 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 the jagged array as an array of arrays. 9.2.4 Array ConversionsConversion is possible between arrays if their dimensions are equal and if a conversion is possible between the element types. An implicit conversion can occur if the elements can be implicitly converted; otherwise an explicit conversion is required. If an array contains references to reference objects, a conversion is possible to an array of base elements. Example 9-7 illustrates the conversion of an array of user-defined Employee types to an array of objects. Example 9-7. Converting arraysnamespace Programming_CSharp { using System; // 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 implicit 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 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.5 System.ArrayThe Array class has a number of useful methods that extend the capabilities of arrays and make them smarter than arrays seen in other languages (see Table 9-1 earlier in this chapter). Two useful static methods of Array are Sort( ) and Reverse( ). These are fully supported for 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 "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.Reversenamespace Programming_CSharp { using System; 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 |
[ Team LiB ] |