The
foreach
looping
statement is new to the C family of languages, though it is 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 foreach
namespace 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; private int size; } 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+10); }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:
for (int i = 0; i < empArray.Length; i++) { Console.WriteLine(empArray[i].ToString()); }
We now 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.
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 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!
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( )
, which 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
namespace 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
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 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.
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.
To declare a two-dimensional array, you use the following syntax:
type [,] array-name
For example, to declare a two-dimensional rectangular array named
myRectangularArray
that contains two rows and
three columns of integers, you would write:
int [,] myRectangularArray;
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 arrays
namespace 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;
which 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]
,
rectangularArray[0,2]
. Once this is done, the
program moves on to the next row: 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
namespace 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 4x3 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 3x4 array.
You can see that the C# compiler understands the implications of your clustering, as 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: An exception of type System.IndexOutOfRangeException was thrown. at Programming_CSharp.Tester.Main( ) in csharpprogramming csharplisting0703.cs:line 23
C# arrays are smart and they keep track of their bounds. When you imply a 4x3 array, you must treat it as such.
A 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 you 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;
You 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 array
namespace 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 accessed 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.
Conversion 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 implicitly be converted, and an explicit conversion is possible; the elements can be explicitly converted.
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
Button
types to an array of objects.
Example 9-7. Converting arrays
namespace 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;
private int size;
}
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 vaue
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
PrintMyArray()
, which takes as a parameter a
one-dimensional array of Object
s:
public static void PrintMyArray(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 PrintMyArray( )
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
PrintMyArray()
method to your advantage, printing
an identifying name of the array:
Contents of the Array Programming_CSharp. Employee [] Contents of the Array System.String[]
PrintMyArray()
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
and so this 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.
The
Array
class has a number of useful methods which 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
Button
is a bit trickier, as you must implement a
number of interfaces (discussed in Chapter 8).
Example 9-8 demonstrates the use of these two
methods to manipulate String
objects.
Example 9-8. Using Array.Sort and Array.Reverse
namespace 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("
");
}
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",
which 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
13.59.79.176