C# Arraays
C# Arraays
STRUCT
A struct is a simple user-defined type, a lightweight alternative to a class.
Structs are similar to classes in that they may contain constructors,
properties, methods, fields, operators, nested types, and indexers.
There are also significant differences between classes and structs.
For instance, structs don’t support inheritance or destructors.
More important, although a class is a reference type, a struct is a value type.
Thus, structs are useful for representing objects that don’t require reference
semantics.
C++ programmers take note: the meaning of C#’s struct construct
is very different from C++’s.
In C++, a struct is exactly like a class, except that the visibility
(public versus private) is different by default.
In C#, structs are value types, whereas classes are reference types,
and C# structs have other limitations
if you have a class that has, as its member variables, 10 structs instead of 10
objects, when that class is created on the heap, one big object is created (the
class with its 10 structs) rather than 11 objects.
That allows the garbage collector to do much less work when your
containing class is ready to be destroyed, making your program more
efficient.
If you have a lot of classes like that, and you create and destroy them
frequently, the performance differences can begin to be noticeable.
The syntax for declaring a struct is almost identical to that for a class:
[attributes] [access-modifiers] struct identifier [:interface-list]
{ struct-members }
No destructor or custom default constructor Structs can’t have destructors,
nor can they have a custom parameterless (default) constructor;
however, the CLR will initialize your structure and zero out all the fields if
your object is called as though it had a default constructor.
No initialization
You can’t initialize an instance field in a struct. Thus, it is illegal to write:
private int xVal = 50;
private int yVal = 100;
though that would have been fine had this been a class.
Structs are designed to be simple and lightweight.
Encapsulating Data with Properties
Properties allow clients to access class state as though they were accessing
member fields directly, while actually implementing that access through a
class method.
This is ideal. The client wants direct access to the state of the object and
doesn’t want to work with methods.
The class designer, however, wants to hide the internal state of his class in
class members, and provide indirect access through a method.
Properties meet both goals:
they provide a simple interface to the client, appearing to be a member
variable.
They are implemented as methods, however, providing the data-hiding
required by good object-oriented design
To declare a property, write the property type and name followed by a pair of
braces.
Within the braces you may declare get and set accessors.
Neither of these has explicit parameters, though the set( ), accessor has an
implicit parameter value.
Hour is a property. Its declaration creates two accessors: get and set:
public int Hour
{
get
{
return hour;
}
set
{
hour = value;
}
}
Each accessor has an accessor body that does the work of retrieving
and setting the property value.
The property value might be stored in a database (in which case the
accessor body would do whatever work is needed to interact with
the database), or it might just be stored in a private member
variable:
private int hour;
The set Accessor
The set accessor sets the value of a property and is similar to a method that
returns void.
When you define a set accessor, you must use the value keyword to represent
the argument whose value is passed to and stored by the property:
set
{
hour = value;
}
Here, again, a private member variable is used to store the value of the
property, but the set accessor could write to a database or update other
member variables as needed.
When you assign a value to the property, the set accessor is automatically
invoked, and the implicit parameter value is set to the value you assign:
theHour++;
t.Hour = theHour;
The two main advantages of this approach are that the client can interact with
the properties directly, without sacrificing the data-hiding and encapsulation
in good object-oriented design, and that the author of the property can ensure
that the data provided is valid.
Property Access Modifiers
It is possible to set an access modifier (protected, internal, private) to modify
access to either the get or set accessor.
To do so, your property must have both a set and a get accessor, and you may
modify only one or the other.
Also, the modifier must be more restrictive than the accessibility level
already on the property or the indexer (thus, you may add protected to the get
or set accessor of a public property, but not to a private property):
public string MyString
{
protected get { return myString; }
set { myString = value; }
}
access to the get accessor is restricted to methods of this class and classes
derived from this class, whereas the set accessor is publicly visible.
readonly Fields
You might want to create a version of the Time class that is responsible for
providing public static values representing the current time and date.
As the example shows, the RightNow.Year value can be changed, for example,
to 2008.
This is clearly not what we’d like. You’d like to mark the static values as
constant, but that is not possible because you don’t initialize them until the
static constructor is executed.
C# provides the keyword readonly for exactly this purpose. If you change the
class member variable declarations as follows:
public static readonly int Year;
public static readonly int Month;
public static readonly int Date;
public static readonly int Hour;
public static readonly int Minute;
public static readonly int Second;
and then comment out the reassignment in Main( ):
// RightNow.Year = 2008; // error!
Arrays
An array is a collection of variables of the same type that are referred to by a
common name.
In C#, arrays can have one or more dimensions, although the one-
dimensional array is the most common.
Arrays are used for a variety of purposes because they offer a convenient
means of grouping together related variables.
For example, you might use an array to hold a record of the daily high
temperature for a month, a list of stock prices, or your collection of
programming books.
The principal advantage of an array is that it organizes data in such a way
that it can be easily manipulated.
For example, if you have an array containing the dividends for a selected
group of stocks, it is easy to compute the average income by cycling through
the array.
Also, arrays organize data in such a way that it can be easily sorted.
Arrays represent a set of items all belonging to the same type.
The declaration itself may use a variable or a constant to define the
length of the array.
However, an array has a set length and it cannot be changed after
declaration.
C# provides native syntax for the declaration of Arrays. What is
actually created, however, is an object of type System.Array.*
Arrays in C# thus provide you with the best of both worlds: easy-
to-use C-style syntax underpinned with an actual class definition so
that instances of an array have access to the methods and properties
of System.Array.
arrays can be declared as fixed length or dynamic.
A fixed length array can store a predefined number of items.
A dynamic array does not have a predefined size.
The size of a dynamic array increases as you add new items to the
array.
You can declare an array of fixed length or dynamic.
You can even change a dynamic array to static after it is defined.
The following code snippet defines the simplest dynamic array of
integer types that does not have a fixed size.
int[] intArray;
As you can see from the above code snippet, the declaration of an
array starts with a type of array followed by a square bracket ([])
and name of the array.
The following code snippet declares an array that can store 5 items
only starting from index 0 to 4.
int[] intArray;
intArray = new int[5];
The following code snippet declares an array that can store 100
items starting from index 0 to 99.
int[] intArray;
intArray = new int[100];
In C#, arrays are objects. That means that declaring an array doesn't
create an array. After declaring an array, you need to instantiate an
array by using the "new" operator.
The following code snippet defines arrays of double, char, bool, and
string data types.
double[] doubleArray = new double[5];
char[] charArray = new char[5];
bool[] boolArray = new bool[2];
string[] stringArray = new string[10];
Once an array is declared, the next step is to initialize an array. The
initialization process of an array includes adding actual data to the
array.
The following code snippet creates an array of 3 items and values
of these items are added when the array is initialized.
// Initialize a fixed array
int[] staticIntArray = new int[3] {1, 3, 5};
Alternative, we can also add array items one at a time as listed in
the following code snippet.
// Initialize a fixed array one item at a time
int[] staticIntArray = new int[3];
staticIntArray[0] = 1;
staticIntArray[1] = 3;
staticIntArray[2] = 5;
The following code snippet declares a dynamic array with string
values.
// Initialize a dynamic array items during declaration
string[] strArray = new string[] { "Mahesh Chand", "Mike
Gold", "Raj Beniwal", "Praveen Kumar", "Dinesh Beniwal" };
We can access an array item by passing the item index in the array.
The following code snippet creates an array of three items and
displays those items on the console.
// Initialize a fixed array one item at a time
int[] staticIntArray = new int[3];
staticIntArray[0] = 1;
staticIntArray[1] = 3;
staticIntArray[2] = 5;
// Read array items one by one
Console.WriteLine(staticIntArray[0]);
Console.WriteLine(staticIntArray[1]);
Console.WriteLine(staticIntArray[2]);
This method is useful when you know what item you want to
access from an array. If you try to pass an item index greater than
the items in array, you will get an error.
The foreach control statement (loop) is used to iterate through the
items of an array. For example, the following code uses foreach
loop to read all items of an array of strings.
// Initialize a dynamic array items during declaration
string[] strArray = new string[] { "Mahesh Chand", "Mike
Gold", "Raj Beniwal", "Praveen Kumar", "Dinesh Beniwal" };
// Read array items using foreach loop
foreach (string str in strArray)
{
Console.WriteLine(str);
}
This approach is used when you do not know the exact index of an
item in an array and needs to loop through all the items.
A multi dimension array is declared as following:
string[,] mutliDimStringArray;
A multi-dimensional array can be fixed-sized or dynamic sized.
Initializing multi-dimensional arrays
The following code snippet is an example of fixed-sized multi-dimensional
arrays that defines two multi dimension arrays with a matrix of 3x2 and 2x2.
The first array can store 6 items and second array can store 4 items. Both of
these arrays are initialized during the declaration.
int[,] numbers = new int[3, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
string[,] names = new string[2, 2] { { "Rosy", "Amy" },
{ "Peter", "Albert" } };
examples of multi-dimensional dynamic arrays where you are not sure of the
number of items of the array. The following code snippet creates two multi-
dimensional arrays with no limit.
int[,] numbers = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
string[,] names = new string[,] { { "Rosy", "Amy" }, { "Peter", "Albert" } };
You can also omit the new operator as we did in single dimension
arrays. You can assign these values directly without using the new
operator. For example:
int[,] numbers = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
string[,] names = { { "Rosy", "Amy" }, { "Peter", "Albert" } };
We can also initialize the array items one item at a time. The
following code snippet is an example of initializing array items one
at a time.
int[,] numbers = new int[3, 2];
numbers[0, 0] = 1;
numbers[1, 0] = 2;
numbers[2, 0] = 3;
numbers[0, 1] = 4;
numbers[1, 1] = 5;
numbers[2, 1] = 6;
A multi-dimensional array items are represented in a matrix format
and to access it's items, we need to specify the matrix dimension.
For example, item(1,2) represents an array item in the matrix at
second row and third column.
The following code snippet shows how to access numbers array
defined in the above code.
Console.WriteLine(numbers[0,0]);
Console.WriteLine(numbers[0, 1]);
Console.WriteLine(numbers[1, 0]);
Console.WriteLine(numbers[1, 1]);
Console.WriteLine(numbers[2, 0]);
Console.WriteLine(numbers[2, 2]);
Jagged Arrays
Jagged arrays are arrays of arrays. The elements of a jagged array are other
arrays.
Declaring Jagged Arrays
Declaration of a jagged array involves two brackets. For example, the
following code snippet declares a jagged array that has three items of an
array.
int[][] intJaggedArray = new int[3][];
The following code snippet declares a jagged array that has two items of an
array.
string[][] stringJaggedArray = new string[2][];
Initializing Jagged Arrays
Before a jagged array can be used, its items must be initialized. The
following code snippet initializes a jagged array; the first item with an array
of integers that has two integers, second item with an array of integers that
has 4 integers, and a third item with an array of integers that has 6 integers.
// Initializing jagged arrays
intJaggedArray[0] = new int[2];
intJaggedArray[1] = new int[4];
intJaggedArray[2] = new int[6];
We can also initialize a jagged array's items by providing the values of the
array's items. The following code snippet initializes item an array's items
directly during the declaration.
// Initializing jagged arrays
intJaggedArray[0] = new int[2]{2, 12};
intJaggedArray[1] = new int[4]{4, 14, 24, 34};
intJaggedArray[2] = new int[6] {6, 16, 26, 36, 46, 56 };
Accessing Jagged Arrays
We can access a jagged array's items individually in the following way:
Console.Write(intJaggedArray3[0][0]);
Console.WriteLine(intJaggedArray3[2][5]);
We can also loop through all of the items of a jagged array. The Length
property of an array helps a lot; it gives us the number of items in an array.
The following code snippet loops through all of the items of a jagged array
and displays them on the screen.
// Loop through all items of a jagged array
for (int i = 0; i < intJaggedArray3.Length; i++)
{
System.Console.Write("Element({0}): ", i);
for (int j = 0; j < intJaggedArray3[i].Length; j++)
{
System.Console.Write("{0}{1}", intJaggedArray3[i][j], j ==
(intJaggedArray3[i].Length - 1) ? "" : " ");
}
System.Console.WriteLine();
}
Array Class
Array class is the mother of all arrays and provides functionality for creating,
manipulating, searching, and sorting arrays in .NET Framework.
Array class, defined in the System namespace, is the base class for arrays in
C#. Array class is an abstract base class that means we cannot create an
instance of the Array class.
Creating an Array
Array class provides the CreateInstance method to construct an array. The
CreateInstance method takes first parameter as the type of items and second
and third parameters are the dimension and their range. Once an array is
created, we use SetValue method to add items to an array.
The following code snippet creates an array and adds three items to the
array. As you can see the type of the array items is string and range is 3. You
will get an error message if you try to add 4thitem to the array.
Array stringArray = Array.CreateInstance(typeof(String), 3);
stringArray.SetValue("Mahesh Chand", 0);
stringArray.SetValue("Raj Kumar", 1);
stringArray.SetValue("Neel Beniwal", 2);
Note: Calling SetValue on an existing item of an array overrides the previous
item value with the new value.
The code snippet in Listing 2 creates a multi-dimensional array.
Array intArray3D = Array.CreateInstance(typeof(Int32), 2, 3, 4);
for (int i = intArray3D.GetLowerBound(0); i <= intArray3D.GetUpperBound(0);
i++)
for (int j = intArray3D.GetLowerBound(1); j <=
intArray3D.GetUpperBound(1); j++)
for (int k = intArray3D.GetLowerBound(2); k <=
intArray3D.GetUpperBound(2); k++)
{
intArray3D.SetValue((i * 100) + (j * 10) + k, i, j, k);
}
foreach (int ival in intArray3D)
{
Console.WriteLine(ival);
}
The code snippet in Listing 3 creates an array and uses Array
properties to display property values.
int[] intArray = new int[3] {0, 1, 2};
if(intArray.IsFixedSize)
{
Console.WriteLine("Array is fixed size");
Console.WriteLine("Size :" + intArray.Length.ToString());
Console.WriteLine("Rank :" + intArray.Rank.ToString());
}
Method or property Purpose
AsReadOnly() Public static method that returns a read-only instance
for a given array
FindLastIndex() Overloaded public static method that returns the index of the last element
that matches a condition
ForEach() Public static method that performs an action on all elements of an array
LongLength Public property that returns the length of the array as a 64-bit integer
Initializes all values in a value type array by calling the default constructor for each value; with
Initialize() reference arrays, all elements in the array are set to null
IsFixedSize Required because Arrayimplements ICollection; with arrays, this will always return true (all arrays are of
a fixed size)
IsReadOnly Public property (required because Arrayimplements IList) that returns a Boolean value indicating
whether the array is read-only
IsSynchronized Public property (required because Arrayimplements ICollection) that returns a Boolean value indicating
whether the array is thread-safe
LastIndexOf() Overloaded public static method that returns the index of the last instance of a value in a one-
dimensional array
Length Public property that returns the length of the array
LongLength Public property that returns the length of the array as a 64-bit integer
Rank Public property that returns the number of dimensions of the array
Reverse() Overloaded public static method that reverses the order of the
elements in a one-dimensional array
SetValue() Overloaded public method that sets the specified array elements to a
value
Sort() Overloaded public static method that sorts the values in a one-
dimensional array
SyncRoot Public property that returns an object that can be used to synchronize
access to the array
TrueForAll() Public static method that checks whether all elements match a
condition
Searching for an Item in an Array
The BinarySearch static method of Array class can be used to search for an
item in an array.
This method uses the binary search algorithm to search for an item.
The method takes at least two parameters.
First parameter is the array in which you would like to search and the second
parameter is an object that is the item you are looking for.
If an item is found in the array, the method returns the index of that item
(based on first item as 0th item).
Otherwise method returns a negative value.
Note: You must sort an array before searching. See comments in this article.
Listing uses BinarySearch method to search an array for a string.
// Create an array and add 5 items to it
Array stringArray = Array.CreateInstance(typeof(String), 5);
stringArray.SetValue("Mahesh", 0);
stringArray.SetValue("Raj", 1);
stringArray.SetValue("Neel", 2);
stringArray.SetValue("Beniwal", 3);
stringArray.SetValue("Chand", 4);
// Find an item
object name = "Neel";
int nameIndex = Array.BinarySearch(stringArray, name);
if (nameIndex >= 0)
Console.WriteLine("Item was at " + nameIndex.ToString() + "th position");
else
Console.WriteLine("Item not found");
Sorting Items in an Array
The Sort static method of the Array class can be used to sort array
items. This method has many overloaded forms. The simplest form
takes as a parameter the array you want to sort. Listing 5 uses the
Sort method to sort array items. Using the Sort method, you can
also sort a partial list of items.
// Create an array and add 5 items to it
Array stringArray = Array.CreateInstance(typeof(String), 5);
stringArray.SetValue("Mahesh", 0);
stringArray.SetValue("Raj", 1);
stringArray.SetValue("Neel", 2);
stringArray.SetValue("Beniwal", 3);
stringArray.SetValue("Chand", 4);
// Find an item
object name = "Neel";
int nameIndex = Array.BinarySearch(stringArray, name);
if (nameIndex >= 0)
Console.WriteLine("Item was at " + nameIndex.ToString() + "th position");
else
Console.WriteLine("Item not found");
Console.WriteLine();
Console.WriteLine("Original Array");
Console.WriteLine("---------------------");
foreach (string str in stringArray)
{
Console.WriteLine(str);
}
Console.WriteLine();
Console.WriteLine("Sorted Array");
Console.WriteLine("---------------------");
Array.Sort(stringArray);
foreach (string str in stringArray)
{ Console.WriteLine(str);}
Although arrays in C# can be used just like arrays in many other
programming languages, they have one special attribute:
They are implemented as objects.
This fact is one reason that a discussion of arrays was deferred until objects
had been introduced.
By implementing arrays as objects, several important advantages are gained,
not the least of which is that unused arrays can be garbage-collected.
One-Dimensional Arrays
A one-dimensional array is a list of related variables. Such lists are common
in programming.
For example, you might use a one-dimensional array to store the account
numbers of the active users on a network.
Another array might store the current batting averages for a Cricket team.
Because arrays in C# are implemented as objects, two steps are needed to
obtain an array for use in your program.
First, you must declare a variable that can refer to an array.
Second, you must create an instance of the array by use of new.
Therefore, to declare a one dimensional array, you will typically use this
general form:
type[ ] array-name = new type[size];
Here, type declares the element type of the array.
The element type determines the data type of each element that comprises the
array. Notice the square brackets that follow type.
They indicate that a one-dimensional array is being declared.
The number of elements that the array will hold is determined by size.
If you come from a C or C++ background, pay special attention to
the way arrays are declared.
Specifically, the square brackets follow the type name, not the array
name.
The following creates an int array of ten elements and links it to an array
reference variable named sample.
int[] sample = new int[10];
The sample variable holds a reference to the memory allocated by new.
This memory is large enough to hold ten elements of type int.
As is the case when creating an instance of a class, it is possible to break the
preceding declaration in two. For example:
int[] sample;
sample = new int[10];
In this case, when sample is first created, it refers to no physical object. It
is only after the second statement executes that sample refers to an array.
An individual element within an array is accessed by use of an index.
An index describes the position of an element within an array.
In C#, all arrays have 0 as the index of their first element.
Because sample has 10 elements, it has index values of 0 through 9. To
index an array, specify the number of the element you want, surrounded by
square brackets.
Thus, the first element in sample is sample[0], and the last element is
sample[9].
For example, the following program loads sample with the numbers 0
through 9:
// Demonstrate a one-dimensional array.
using System;
class ArrayDemo {
static void Main() {
int[] sample = new int[10];
int i;
for(i = 0; i < 10; i = i+1)
sample[i] = i;
for(i = 0; i < 10; i = i+1)
Console.WriteLine("sample[" + i + "]: " + sample[i]);
}
}
The output from the program is shown here:
sample[0]: 0
sample[1]: 1
sample[2]: 2
Conceptually, the sample array looks like this:
You declare a C# array with the following syntax:
type[] array-name;
For example:
int[] myIntArray;
You aren’t actually declaring an array.
Technically, you are declaring a variable (myIntArray) that will
hold a reference to an array of integers.
As always, we’ll use the shorthand and refer to myIntArray as the
array, knowing that we really mean a variable that holds a reference
to an (unnamed) array.
The square brackets ([]) tell the C# compiler that you are declaring an array,
and the type specifies the type of the elements it will contain.
In the previous example, myIntArray is an array of integers.
You instantiate an array by using the new keyword.
For example: myIntArray = new int[5];
This declaration creates and initializes an array of five integers, all of which
are initialized to the value 0.
VB 6 programmers take note: in C#, the value of the size of the
array marks the number of elements in the array, not the upper
bound.
In fact, there is no way to set the upper or lower bound—with the
exception that you can set the lower bounds in multidimensional
arrays , but even that is not supported by the .NET Framework class
library.
Thus, the first element in an array is 0. The following C# statement
declares an array of 10 elements, with indexes 0 through 9:
string myArray[10];
The upper bound is 9, not 10, and you can’t change the size of the
array (i.e., there is no equivalent to the VB 6 Redim function).
arrays are objects and thus have properties.
One of the more useful of these is Length, which tells you how
many objects are in an array.
Array objects can be indexed from 0 to Length-1.
That is, if there are five elements in an array, their indexes are 0, 1,
2, 3, 4.
// Compute the average of a set of values.
using System;
class Average {
static void Main() {
int[] nums = new int[10];
int avg = 0;
nums[0] = 99;
nums[1] = 10;
nums[2] = 100;
nums[3] = 18;
nums[4] = 78;
nums[5] = 23;
nums[6] = 63;
nums[7] = 9;
nums[8] = 87;
nums[9] = 49;
for(int i=0; i < 10; i++)
avg = avg + nums[i];
avg = avg / 10;
Console.WriteLine("Average: " + avg); } }
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 note on syntaxes.
Both syntaxes exist because in some rare circumstances, you have
to use the longer syntax—specifically, if the C# compiler is unable
to infer the correct type for the array.
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 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 though 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);
Boundaries Are Enforced
Array boundaries are strictly enforced in C#; it is a runtime error to overrun
or underrun the ends of an array.
Multidimensional Arrays
You can think of an array as a long row of slots into which you can place
values.
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.
C# supports two types of multidimensional arrays:
rectangular
In a rectangular array, every row is the same length.
jagged
A jagged array, however, is an array of arrays, each of which can be a different
length.
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.
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];
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];
The brackets in the int[,] declaration indicate that the type is an array of
integers, and the comma indicates that the array has two dimensions (two
commas would indicate three dimensions, etc.).
The actual instantiation of rectangularArray with new int[rows, columns] sets the size
of each dimension.
Here, the declaration and instantiation have been combined.
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 declares a two-dimensional array (rectangularArray), initializes its
elements using bracketed lists of values, and then prints the contents.
type[,] array_name = {
{ val, val, val, ..., val },
{ val, val, val, ..., val },
...
{ val, val, val, ..., val }
};
Here, val indicates an initialization value. Each inner block designates a row.
Within each row, the first value will be stored in the first position, the second
value in the second position, and so on.
Notice that commas separate the initializer blocks and that a semicolon
follows the closing }.
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 because it can access the objects with the appropriate offsets, as
illustrated in the output.
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.
A jagged array is an array of arrays in which the length of each array can
differ.
Thus, a jagged array can be used to create a table in which the lengths of the
rows are not the same.
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 [] []...
Jagged arrays are declared by using sets of square brackets to indicate each
dimension.
For example, to declare a two-dimensional jagged array, you will use this
general form:
type[ ] [ ] array-name = new type[size][ ];
Here, size indicates the number of rows in the array.
The rows, themselves, have not been allocated. Instead, the rows are allocated
individually.
This allows for the length of each row to vary.
For example, the following code allocates memory for the first dimension of
jagged when it is declared.
It then allocates the second dimensions manually.
int[][] jagged = new int[3][];
jagged[0] = new int[4];
jagged[1] = new int[3];
jagged[2] = new int[5];
After this sequence executes, jagged looks like this:
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];
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 their contents to ensure that all went as
expected.
Notice that when you access the members of a rectangular array, you put the
indexes all within one set of square brackets:
rectangularArrayrectangularArray[i,j]
whereas with a jagged array you need a pair of brackets:
jaggedArray[3][i]
You can keep this straight by thinking of the first array as a single array of
more than one dimension, and the jagged array as an array of arrays.
Assigning Array References
As with other objects, when you assign one array reference variable to
another, you are simply making both variables refer to the same array.
You are neither causing a copy of the array to be created, nor are you causing
the contents of one array to be copied to the other.
Using the Length Property
A number of benefits result because C# implements arrays as objects. One
comes from the fact that each array has associated with it a Length property
that contains the number of elements that an array can hold.
Thus, each array provides a means by which its length can be determined.
static void Main() {
int[] nums = new int[10];
Console.WriteLine("Length of nums is " + nums.Length);
// Use Length to initialize nums.
for(int i=0; i < nums.Length; i++)
nums[i] = i * i;
// Now use Length to display nums.
Console.Write("Here is nums: ");
for(int i=0; i < nums.Length; i++)
Console.Write(nums[i] + " ");
Console.WriteLine();
Using Length with Jagged Arrays
A special case occurs when Length is used with jagged arrays.
In this situation, it is possible to obtain the length of each individual array.
class Jagged {
static void Main() {
int[][] network_nodes = new int[4][];
network_nodes[0] = new int[3];
network_nodes[1] = new int[7];
network_nodes[2] = new int[2];
network_nodes[3] = new int[5];
int i, j;
// Fabricate some fake CPU usage data.
for(i=0; i < network_nodes.Length; i++)
for(j=0; j < network_nodes[i].Length; j++)
network_nodes[i][j] = i * j + 70;
Console.WriteLine("Total number of network nodes: " +
network_nodes.Length + "\n");
for(i=0; i < network_nodes.Length; i++) {
for(j=0; j < network_nodes[i].Length; j++) {
Console.Write("CPU usage at node " + i +
" CPU " + j + ": ");
Console.Write(network_nodes[i][j] + "% ");
Console.WriteLine();
}
Console.WriteLine();
The output is shown here:
Total number of network nodes: 4
CPU usage at node 0 CPU 0: 70%
CPU usage at node 0 CPU 1: 70%
CPU usage at node 0 CPU 2: 70%
CPU usage at node 1 CPU 0: 70%
CPU usage at node 1 CPU 1: 71%
CPU usage at node 1 CPU 2: 72%
CPU usage at node 1 CPU 3: 73%
CPU usage at node 1 CPU 4: 74%
CPU usage at node 1 CPU 5: 75%
CPU usage at node 1 CPU 6: 76%
CPU usage at node 2 CPU 0: 70%
CPU usage at node 2 CPU 1: 72%
CPU usage at node 3 CPU 0: 70%
CPU usage at node 3 CPU 1: 73%
CPU usage at node 3 CPU 2: 76%
CPU usage at node 3 CPU 3: 79%
CPU usage at node 3 CPU 4: 82%
Pay special attention to the way Length is used on the jagged array
network_nodes.
Recall, a two-dimensional jagged array is an array of arrays.
Thus, when the expression
network_nodes.Length
is used, it obtains the number of arrays stored in network_nodes, which is
four in this case.
To obtain the length of any individual array in the jagged array, you will use
an expression such as this:
network_nodes[0].Length
which, in this case, obtains the length of the first array.
Implicitly Typed Arrays
The ability to declare implicitly typed variables by using the var keyword.
These are variables whose type is determined by the compiler, based on the
type of the initializing expression.
Thus, all implicitly typed variables must be initialized.
Using the same mechanism, it is also possible to create an implicitly typed
array.
An implicitly typed array is declared using the keyword var, but you do not
follow var with [ ].
Furthermore, the array must be initialized because it is the type of the
initializers that determine the element type of the array.
All of the initializers must be of the same or compatible type.
Here is an example of an implicitly typed array:
var vals = new[] { 1, 2, 3, 4, 5 };
This creates an array of int that is five elements long.
A reference to that array is assigned to vals.
Thus, the type of vals is “array of int” and it has five elements.
Again, notice that var is not followed by [ ].
Also, even though the array is being initialized, you must include new[ ].
It’s not optional in this context.
Here is another example. It creates a two-dimensional array of double:
var vals = new[,] { {1.1, 2.2}, {3.3, 4.4},{ 5.5, 6.6} };
In this case, vals has the dimensions 2×3.
Indexers
Sometimes you may need to access a collection within a class as though the
class itself were an array.
For example, suppose you create a listbox control named myListBox that
contains a list of strings stored in a one-dimensional array, a private member
variable named myStrings.
A listbox control contains member properties and methods in addition to its
array of strings.
However, it would be convenient to be able to access the listbox array with
an index, just as though the listbox were an array.*
For example, such a property would permit statements like the following:
string theFirstString = myListBox[0];
string theLastString = myListBox[Length-1];
An indexer is a C# construct that allows you to access collections contained
by a class using the familiar [] syntax of arrays.
An indexer is a special kind of property, and includes get and set accessors to
specify its behavior.
You declare an indexer property within a class using the following syntax:
type this [type argument]{get; set;}
The return type determines the type of object that will be returned by the
indexer, whereas the type argument specifies what kind of argument will be
used to index into the collection that contains the target objects.
Although it is common to use integers as index values, you can index a
collection on other types as well, including strings.
You can even provide an indexer with multiple parameters to create a
multidimensional array!
The this keyword is a reference to the object in which the indexer appears.
As with a normal property, you also must define get and set accessors, which
determine how the requested object is retrieved from or assigned to its
collection.
C++ programmers take note: the indexer serves much the same purpose as
overloading the C++ index operator ([]).
The index operator can’t be overloaded in C#, which provides the indexer in
its place.
you can’t assign to an index that doesn’t have a value. So, if you write:
lbt[10] = "wow!";
you will trigger the error handler in the set( ) method, which will note that the
index you’ve passed in (10) is larger than the counter (6).
Of course, you can use the set( ) method for assignment; you simply have to
handle the indexes you receive.
To do so, you might change the set( ) method to check the Length of the
buffer rather than the current value of counter.
If a value was entered for an index that did not yet have a value, you would
update ctr:
set
{
// add only through the add method
if (index >= strings.Length )
{
// handle error
}
else
{
strings[index] = value;
if (ctr < index+1)
ctr = index+1;
}
}
C# doesn’t require that you always use an integer value as the index to a
collection.
When you create a custom collection class and create your indexer, you are
free to create indexers that index on strings and other types.
In fact, the index value can be overloaded so that a given collection can be
indexed, for example, by an integer value or by a string value, depending on
the needs of the client.
In the case of your listbox, you might want to be able to index into the listbox
based on a string.
The indexer calls findString( ), which is a helper method that returns a record
based on the value of the string provided.
The careful reader will note that if the string doesn’t match, a value of -1 is
returned, which is then used as an index into myStrings.
This action then generates an exception (System.NullReferenceException),
as you can see by uncommenting the following line in Main( ): lbt["xyz"] =
"oops";
The proper handling of not finding a string is, as they say, left as an exercise
for the reader.
You might consider displaying an error message or otherwise allowing the
user to recover from the error.
public ListBoxTest(params string[] initialStrings)
{
// allocate space for the strings
strings = new String[256];
// copy the strings passed in to the constructor
foreach (string s in initialStrings)
{
strings[ctr++] = s;
}
}
the listbox maintains a simple array of strings: strings.
Again, in a real listbox, you might use a more complex and dynamic
container, such as a hash table.
The member variable ctr will keep track of how many strings have been
added to this array.
Initialize the array in the constructor with the statement:
strings = new String[256];
The remainder of the constructor adds the parameters to the array.
Again, for simplicity, you add new strings to the array in the order received.
Because you can’t know how many strings will be added, you use the
keyword params.
public void Add(string theString)
{
if (ctr >= strings.Length)
{
// handle bad index
}
else
strings[ctr++] = theString;
}
// allow array-like access
public string this[int index]
{
get
{
if (index < 0 || index >= strings.Length)
{
// handle bad index
}
return strings[index];
}
The Add( ) method of ListBoxTest does nothing more than append a new
string to the internal array.
The key method of ListBoxTest, however, is the indexer.
An indexer is unnamed, so use the this keyword:
public string this[int index]
The syntax of the indexer is very similar to that for properties.
There is either a get( ) method, a set( ) method, or both.
In the case shown, the get( ) method endeavors to implement rudimentary
bounds-checking, and assuming the index requested is acceptable, it returns
the value requested
set
{
// add only through the add method
if (index >= ctr)
{
// handle error
}
else
strings[index] = value;
}
}
// publish how many strings you hold
public int GetNumEntries( )
{
return ctr;
}
}
The set( ) method checks to make sure that the index you are setting already
has a value in the listbox.
If not, it treats the set as an error. (New elements can only be added using
Add with this approach.)
The set accessor takes advantage of the implicit parameter value that
represents whatever is assigned using the index operator.
Thus, if you write:
lbt[5] = "Hello World"
the compiler will call the indexer set( ) method on your object and pass in the
string Hello World as an implicit parameter named value.
Strings
Strings in C# are objects and derive from System.String, alias string.
Each string is an immutable sequence of zero or more characters.
Any attempt, therefore, to change a string via one of its methods creates an
entirely new string object.
A string is often initialized to a string literal, a sequence of zero or more
characters enclosed in double quotes, such as "Csharp".
A string is also zero-based.
Hence, the first character of a string is designated at index 0.
Each manipulation of an immutable string created by System.String results in a
new string object being allocated on the heap.
Many of these immutable strings will be unreachable and eventually garbage
collected. For example:
1 string myName = "Michel";
2
3 myName = String.Concat(myName, " de");
4 myName = String.Concat(myName, " Champlain");
The above concatenation has instantiated five strings:
three for the literal strings ("Michel", " de", and " Champlain"),
one as the result of the concatenation on line 3 ("Michel de"),
and another as the last concatenation on line 4 ("Michel de Champlain").
Repeating concatenations or making intensive manipulations on immutable
strings within loops may be very inefficient.
To improve performance, the StringBuilder class in the Tip namespace
System.Text is a better choice.
It represents instead mutable strings that are allocated only once on the heap.
An object of the StringBuilder class allows a string of up to 16 characters by
default and grows dynamically as more characters are added.
Its maximum size may be unbounded or increased to a configurable
maximum.
This example shows the equivalent concatenation of strings using the
Append method:
StringBuilder myName = new StringBuilder("Michel");
myName.Append(" de");
myName.Append(" Champlain");
The three literal strings are still allocated, but only one StringBuilder object
assigned to myName is allocated and reused.
In addition to methods such as Insert, Remove, and Replace, the
StringBuilder class is equipped with a number of overloaded constructors:
DateTime
Time representations are complicated.
The C# language provides the DateTime type.
This type can be used to get the current time and relative times such as
yesterday, tomorrow and certain days in years.
DateTime offers many useful methods and properties for date and time
computations.
Constructor
For the DateTime type in the C# language and .NET Framework, you can use
the instance constructor with the new operator to instantiate a new DateTime
instance.
The arguments to the constructor must match a real date that occurred.
This example also shows how you can write a DateTime to the console.
It also compares a DateTime against the Today value. Console.WriteLine
using System;
class Program
{
static void Main()
{
// This DateTime is constructed with an instance constructor.
// ... We write it to the console.
// ... If this is today, the second line will be "True".
DateTime value = new DateTime(2010, 1, 18);
Console.WriteLine(value);
Console.WriteLine(value == DateTime.Today);
}
}
Find yesterday
Here we see how to subtract one day from the current day.
We do this by adding -1 to the current day.
This is necessary because no "Subtract Days" method is provided.
An extension method could help depending on your style.
using System;
class Program
{
static void Main()
{
Console.WriteLine("Today: {0}", DateTime.Today);
DateTime y = GetYesterday();
Console.WriteLine("Yesterday: {0}", y);
}
/// <summary>
/// Gets the previous day to the current day.
/// </summary>
static DateTime GetYesterday()
{
// Add -1 to now
return DateTime.Today.AddDays(-1);
}
}
Today: 11/30/2008 12:00:00 AM
Yesterday: 11/29/2008 12:00:00 AM
The example displays the current day.
Note that the Console.WriteLine implicitly converts the DateTime.Today
argument using its ToString method.
This would make them reusable across projects.
Here we see how you can add one using the DateTime Add method to figure
out tomorrow.
This is useful for using date queries in databases, as you usually have to
select a range of dates.
using System;
class Program
{
static void Main()
{
Console.WriteLine("Today: {0}", DateTime.Today);
DateTime d = GetTomorrow();
Console.WriteLine("Tomorrow: {0}", d);
}
/// <summary>
/// Gets the next day, tomorrow.
/// </summary>
static DateTime GetTomorrow()
{
return DateTime.Today.AddDays(1);
}
}
Output
Today: 11/30/2008 12:00:00 AM
Tomorrow: 12/1/2008 12:00:00 AM
This method and the previous ones are static methods because they do not
require state to be saved.
DateTime.Today is not an instance of the class.
DateTime.Add uses offsets, meaning it accepts both negative and positive
numbers. Here we go back in time.
Name Description
Compares the value of this instance to a specified object that contains a specified
CompareTo(Object) DateTime value, and returns an integer that indicates whether this instance is
earlier than, the same as, or later than the specified DateTime value.
Returns a value indicating whether the value of this instance is equal to the value
Equals(DateTime) of the specifiedDateTime instance.
Converts the value of this instance to all the string representations supported by
GetDateTimeFormats(Ch the specified standard date and time format specifier.
ar)
Converts the value of this instance to all the string representations supported by
GetDateTimeFormats(IF the standard date and time format specifiers and the specified culture-specific
ormatProvider) formatting information.
Converts the value of this instance to all the string representations supported by
GetDateTimeFormats(Ch the specified standard date and time format specifier and culture-specific
ar, IFormatProvider) formatting information.