Arrays

A fixed-length, zero-indexed run of values of one type: declaring one, reading Length, and staying inside the bounds.

Syntaxint[] scores = new int[4];

An array is a fixed-length run of values, all of the same type, kept in order. You choose the length when you create the array, and that length never changes afterwards. Arrays are the simplest storage the language has, and most other collections are built on one.

Positions start at zero

Each value sits at a numbered position. That number is called an index. The first index is 0, not 1. An array with four slots has indexes 0, 1, 2 and 3. The Length property tells you how many slots there are, so the last valid index is always Length - 1.

A new array is not empty. Every slot already holds a default value: 0 for numbers, false for bool, and null for reference types such as string.

Three ways to write the same thing

All three lines below build the same three-item array of int. They are not different features; the compiler produces the same code for each.

int[] a = new int[] { 1, 2, 3 };
int[] b = { 1, 2, 3 };
int[] c = [1, 2, 3];        // collection expression, C# 12 and later

Where it goes wrong

Reading or writing an index outside the array throws IndexOutOfRangeException while the program runs. The usual cause is a loop that goes up to Length instead of stopping before it, so write the condition as i < array.Length. To reach the last item you can write array[^1], which is shorthand for array[array.Length - 1].

Example

Example · csharp
int[] scores = new int[4];      // four slots, and every one starts at 0

scores[0] = 18;
scores[1] = 25;
scores[2] = 31;
scores[3] = 26;

// Length is the number of slots. Valid indexes run from 0 to Length - 1.
Console.WriteLine($"slots: {scores.Length}");
Console.WriteLine($"first: {scores[0]}");
Console.WriteLine($"last: {scores[scores.Length - 1]}");

int total = 0;

for (int i = 0; i < scores.Length; i++)
{
    total += scores[i];
}

Console.WriteLine($"total: {total}");

// The length comes from the number of values you list.
string[] names = { "ana", "bo", "cy" };
Console.WriteLine($"names: {string.Join(", ", names)}");

// Output:
// slots: 4
// first: 18
// last: 26
// total: 100
// names: ana, bo, cy

When to use it

  • Holding the twelve monthly totals in a report, where the count is known and never changes.
  • Filling a byte buffer of a fixed size before handing it to a file or network call.
  • Passing a batch of values to a method that needs them in a known order.

More examples

Walking an array with foreach

When you only need the values and not their positions, foreach removes the whole class of off-by-one mistakes. Reach for a for loop instead when you need the index itself, or when you want to walk the array backwards.

Example · csharp
double[] temperatures = { 12.5, 15.0, 9.75, 18.25 };

double highest = temperatures[0];   // start from a real reading, not 0
double total = 0;

// foreach hands you each value in order. You never write an index,
// so you cannot get the index wrong.
foreach (double reading in temperatures)
{
    total += reading;

    if (reading > highest)
    {
        highest = reading;
    }
}

double average = total / temperatures.Length;

Console.WriteLine($"highest: {highest}");
Console.WriteLine($"average: {average}");

// Output:
// highest: 18.25
// average: 13.875

An array cannot grow, so you copy into a bigger one

This is the work that List<T> does for you on every Add. Seeing it once explains why a growable collection exists at all, and why an array is the right choice only when you know the count up front.

Example · csharp
string[] names = { "ana", "bo" };

// The length of an array is fixed for its whole life. To "grow" one you
// make a bigger array and copy the old values into it.
string[] bigger = new string[3];
Array.Copy(names, bigger, names.Length);   // copies 2 items into positions 0 and 1
bigger[2] = "cy";

Console.WriteLine($"old length: {names.Length}");
Console.WriteLine($"new: {string.Join(", ", bigger)}");

// Array.IndexOf returns the position, or -1 when the value is not there.
Console.WriteLine($"bo is at: {Array.IndexOf(bigger, "bo")}");
Console.WriteLine($"zed is at: {Array.IndexOf(bigger, "zed")}");

// Output:
// old length: 2
// new: ana, bo, cy
// bo is at: 1
// zed is at: -1

A rectangular array has a position per dimension

Use a rectangular array for a genuine grid, such as a board or a fixed table of numbers. Note that Length counts every cell, so sizing a loop needs GetLength with the dimension you mean.

Example · csharp
// Two rows, three columns, every cell 0.
int[,] grid = new int[2, 3];

// GetLength(0) is the number of rows, GetLength(1) the number of columns.
for (int row = 0; row < grid.GetLength(0); row++)
{
    for (int col = 0; col < grid.GetLength(1); col++)
    {
        grid[row, col] = (row * 3) + col;
    }
}

Console.WriteLine($"rows: {grid.GetLength(0)}, columns: {grid.GetLength(1)}");
Console.WriteLine($"total cells: {grid.Length}");   // Length is every cell, not a side
Console.WriteLine($"grid[1, 2]: {grid[1, 2]}");

// Output:
// rows: 2, columns: 3
// total cells: 6
// grid[1, 2]: 5

Discussion

  • Be the first to comment on this lesson.