Array types
Arrays may be single-dimensional or multi-dimensional. Both “rectangular” and “jagged” arrays are supported.
Single-dimensional arrays are the most common type. The example
class Test
{
static void Main() {
int[] arr = new int[5];
for (int i = 0; i < arr.Length; i++)
arr[i] = i * i;
for (int i = 0; i < arr.Length; i++)
Console.WriteLine("arr[{0}] = {1}", i, arr[i]);
}
}
creates a single-dimensional array of int values, initializes the array elements, and then prints each of them out. The output produced is:
arr[0] = 0
arr[1] = 1
arr[2] = 4
arr[3] = 9
arr[4] = 16
The type int[] used in the previous example is an array type. Array types are written using a non-array-type followed by one or more rank specifiers. The example
class Test
{
static void Main() {
int[] a1; // single-dimensional array of int
int[,] a2; // 2-dimensional array of int
int[,,] a3; // 3-dimensional array of int
int[][] j2; // "jagged" array: array of (array of int)
int[][][] j3; // array of (array of (array of int))
}
}
shows a variety of local variable declarations that use array types with int as the element type.
Array types are reference types, and so the declaration of an array variable merely sets aside space for the reference to the array. Array instances are actually created via array initializers and array creation expressions. The example
class Test
{
static void Main() {
int[] a1 = new int[] {1, 2, 3};
int[,] a2 = new int[,] {{1, 2, 3}, {4, 5, 6}};
int[,,] a3 = new int[10, 20, 30];
int[][] j2 = new int[3][];
j2[0] = new int[] {1, 2, 3};
j2[1] = new int[] {1, 2, 3, 4, 5, 6};
j2[2] = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
}
}
shows a variety of array creation expressions. The variables a1, a2 and a3 denote rectangular arrays, and the variable j2 denotes a jagged array. It should be no surprise that these terms are based on the shapes of the arrays. Rectangular arrays always have a rectangular shape. Given the length of each dimension of the array, its rectangular shape is clear. For example, the lengths of a3’s three dimensions are 10, 20, and 30 respectively, and it is easy to see that this array contains 10*20*30 elements.
In contrast, the variable j2 denotes a “jagged” array, or an “array of arrays”. Specifically, j2 denotes an array of an array of int, or a single-dimensional array of type int[]. Each of these int[] variables can be initialized individually, and this allows the array to take on a jagged shape. The example gives each of the int[] arrays a different length. Specifically, the length of j2[0] is 3, the length of j2[1] is 6, and the length of j2[2] is 9.
The element type and shape of an array—including whether it is jagged or rectangular, and the number of dimensions it has—are part of its type. On the other hand, the size of the array—as represented by the length of each of its dimensions—is not part of an array’s type. This split is made clear in the language syntax, as the length of each dimension is specified in the array creation expression rather than in the array type. For instance the declaration
int[,,] a3 = new int[10, 20, 30];
has an array type of int[,,] and an array creation expression of new int[10, 20, 30].
For local variable and field declarations, a shorthand form is permitted so that it is not necessary to re-state the array type. For instance, the example
int[] a1 = new int[] {1, 2, 3};
can be shortened to
int[] a1 = {1, 2, 3};
without any change in program semantics.
The context in which an array initializer such as {1, 2, 3} is used determines the type of the array being initialized. The example
class Test
{
static void Main() {
short[] a = {1, 2, 3};
int[] b = {1, 2, 3};
long[] c = {1, 2, 3};
}
}
shows that the same array initializer syntax can be used for several different array types. Because context is required to determine the type of an array initializer, it is not possible to use an array initializer in an expression context without explicitly stating the type of the array.
Type system unification
C# provides a “unified type system”. All types—including value types—derive from the type object. It is possible to call object methods on any value, even values of “primitive” types such as int. The example
class Test
{
static void Main() {
Console.WriteLine(3.ToString());
}
}
calls the object-defined ToString method on an integer literal, resulting in the output “3”.
The example
class Test
{
static void Main() {
int i = 123;
object o = i; // boxing
int j = (int) o; // unboxing
}
}
is more interesting. An int value can be converted to object and back again to int. This example shows both boxing and unboxing. When a variable of a value type needs to be converted to a reference type, an object box is allocated to hold the value, and the value is copied into the box. Unboxing is just the opposite. When an object box is cast back to its original value type, the value is copied out of the box and into the appropriate storage location.
This type system unification provides value types with the benefits of object-ness without introducing unnecessary overhead. For programs that don’t need int values to act like objects, int values are simply 32-bit values. For programs that need int values to behave like objects, this capability is available on demand. This ability to treat value types as objects bridges the gap between value types and reference types that exists in most languages. For example, a Stack class can provide Push and Pop methods that take and return object values.
public class Stack
{
public object Pop() {...}
public void Push(object o) {...}
}
Because C# has a unified type system, the Stack class can be used with elements of any type, including value types like int
Arrays wih examples
Labels: Types and Objects
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment