Skip to main content

2.5 - Arrays

Arrays in C# provide a structured method to store sequential collections of elements of the same type. They are essential in many programming scenarios because they offer fast access to data through indexing. This chapter explores the depths of array manipulation, initialization patterns, system array methods, and multidimensional array configurations.

🧩 Visual Learning: What Are Arrays?​

Think of an array as a row of mailboxes in an apartment building:

β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”
β”‚ 0 β”‚ 1 β”‚ 2 β”‚ 3 β”‚ 4 β”‚ 5 β”‚ ← Index positions (always start at 0)
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€
β”‚ 42 β”‚ 17 β”‚ 23 β”‚ 8 β”‚ 91 β”‚ 55 β”‚ ← Values stored in each position
β””β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”˜

Each mailbox:

  • Has a unique number (index) starting from 0
  • Can hold one item (value) of the same type
  • Can be accessed directly if you know its number

πŸ’‘ Concept Breakdown: Why Use Arrays?​

Arrays solve several common programming problems:

  1. Storing related data together - Like test scores for a class of students
  2. Processing multiple items efficiently - Such as calculating the average of those scores
  3. Accessing items by position - For example, finding the 3rd student's score

Real-world examples:

  • A list of temperatures for each day of the week
  • The names of all players on a sports team
  • The prices of products in an online store

2.5.1 - Array Fundamentals​

2.5.1.1 - Definition and Declaration​

An array is a fixed-size sequence of elements of a type. To declare an array, specify the type of its elements and the number of dimensions. Arrays are zero-based, meaning that the index of the first element is zero.

πŸ”° Beginner's Corner: Understanding Array Declaration​

When you declare an array, you're essentially telling C#:

  1. What type of data the array will hold (like int, string, etc.)
  2. That you want a collection of this type (indicated by the square brackets [])
// This is how you declare an array of integers
int[] scores;

This line doesn't create the array yet - it just declares a variable that can hold an array of integers. Think of it like reserving a name for a collection you'll create later.

// Declaring a single-dimensional array of integers (not yet initialized)
int[] singleDimensional;

// Declaring a two-dimensional array of doubles (like a grid or matrix)
double[,] multiDimensional;

// Declaring a jagged array (array of arrays) of booleans
// Each inner array can have a different length
bool[][] jaggedArray;

2.5.1.2 - Initialization​

Arrays can be initialized using the new keyword followed by the type and the number of elements. They can be initialized empty or with a predefined set of elements.

πŸ”° Beginner's Corner: Creating Your First Array​

There are three main ways to create and fill an array:

Method 1: Create an empty array of a specific size

// Create an array that can hold 5 integers
int[] scores = new int[5];

// At this point, all elements have the default value (0 for integers)
// scores is now: [0, 0, 0, 0, 0]

// You can fill it later:
scores[0] = 95; // First element (remember, counting starts at 0)
scores[1] = 88; // Second element
scores[2] = 76; // Third element
scores[3] = 92; // Fourth element
scores[4] = 84; // Fifth element

// Now scores is: [95, 88, 76, 92, 84]

Method 2: Create and fill an array in one step

// Create and fill an array in one statement
string[] fruits = new string[] { "Apple", "Banana", "Cherry" };

// The size is automatically set to match the number of elements (3)

Method 3: Shorthand syntax (most concise)

// Even shorter syntax - the 'new string[]' part is implied
string[] colors = { "Red", "Green", "Blue" };

⚠️ Common Pitfalls for Beginners​

  1. Forgetting that arrays are zero-indexed

    int[] numbers = { 10, 20, 30 };
    // The first element is at index 0, not 1
    Console.WriteLine(numbers[0]); // Outputs: 10
  2. Trying to access an index that doesn't exist

    int[] numbers = { 10, 20, 30 };
    // This will cause an IndexOutOfRangeException:
    // Console.WriteLine(numbers[3]); // There is no element at index 3!
  3. Not initializing the array before using it

    int[] scores;  // Declared but not initialized
    // This would cause an error:
    // Console.WriteLine(scores[0]); // scores is not initialized!
// Initializing with default values
// This creates an array with 10 elements, all initialized to 0 (the default for int)
int[] numbers = new int[10];

// Initializing with specific values
// The array size is determined by the number of elements provided
string[] names = new string[] { "Alice", "Bob", "Charlie" };

// Using the shorthand notation (implicit array creation)
// The compiler infers both the type and size from the initialization
double[] weights = { 50.5, 65.0, 77.25 };

// When you need an empty array (with no elements)
// This is different from null - it's an array with length 0
string[] emptyArray = new string[0]; // or Array.Empty<string>()

πŸ’‘ Concept Breakdown: Default Values​

When you create an array without specifying values, each element gets a default value based on its type:

TypeDefault Value
int, long, etc.0
double, float0.0
boolfalse
char'\0' (null character)
Reference types (string, classes, etc.)null

2.5.1.3 - Array Literals and Implications​

From C# 9, you can use target-typed new expressions, which enhance readability and maintain type safety:

// Target-typed new expression (C# 9+)
// The compiler infers the element type from the variable declaration
int[] array = new[] { 1, 2, 3, 4, 5 };

// This is particularly useful when the type is complex or when working with generics
Dictionary<string, List<int>> map = new() { ["key"] = new() { 1, 2, 3 } };

// You can also use array creation expressions in more complex scenarios
var matrix = new int[][]
{
new[] { 1, 2, 3 },
new[] { 4, 5, 6 },
new[] { 7, 8, 9 }
};

2.5.2 - Working with Array Elements​

2.5.2.1 - Accessing Elements​

Elements are accessed by their index. Attempting to access indices outside the bounds of the array throws an IndexOutOfRangeException.

// Create an array with 5 elements
int[] numbers = { 10, 20, 30, 40, 50 };

// Accessing the first element (index 0)
int firstElement = numbers[0]; // firstElement = 10

// Setting the fifth element (index 4) to 100
numbers[4] = 100; // numbers is now { 10, 20, 30, 40, 100 }

// Using the Length property to get the number of elements
int arraySize = numbers.Length; // arraySize = 5

// Accessing the last element using Length
int lastElement = numbers[numbers.Length - 1]; // lastElement = 100

// C# 8.0+ - Using index from end operator (^)
int lastElementAlt = numbers[^1]; // Same as numbers[numbers.Length - 1]
int secondToLast = numbers[^2]; // Same as numbers[numbers.Length - 2]

// C# 8.0+ - Using ranges to get a slice of the array
int[] middleThree = numbers[1..4]; // Gets elements at indices 1, 2, and 3
// middleThree = { 20, 30, 40 }

// CAUTION: This will throw an IndexOutOfRangeException
try
{
int outOfBounds = numbers[10]; // Error: index 10 is outside the bounds
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Array access error: " + ex.Message);
// Always validate indices before accessing array elements
}

2.5.2.2 - Iterating Over Arrays​

Arrays can be traversed using loops. The foreach loop offers a simple method to iterate over all elements, and the for loop can also be used for this purpose.

int[] numbers = { 1, 2, 3, 4, 5 };

// 1. Using foreach loop - simplest way to iterate (read-only access)
// Best when you need to process each element but don't need to modify the array
Console.WriteLine("Using foreach loop:");
foreach (int number in numbers)
{
// 'number' is a copy of the current element
Console.WriteLine(number);

// Note: You cannot modify the array elements directly in a foreach loop
// number = 10; // This would compile but wouldn't change the array
}

// 2. Using traditional for loop - provides index access and allows modification
// Best when you need the index or need to modify elements
Console.WriteLine("Using for loop:");
for (int i = 0; i < numbers.Length; i++)
{
// We can use the index to both read and write elements
Console.WriteLine($"Element at index {i}: {numbers[i]}");

// We can modify elements
numbers[i] = i * 2; // Multiply each element by 2
}
// After the loop, numbers = { 0, 2, 4, 6, 8 }

// 3. Using LINQ methods (requires System.Linq namespace)
// Best for functional programming style and complex operations
using System.Linq;

Console.WriteLine("Using LINQ:");
// ForEach extension method from LINQ
numbers.ToList().ForEach(number => Console.WriteLine(number));

// 4. Using Array.ForEach method - combines benefits of foreach with ability to use delegates
Console.WriteLine("Using Array.ForEach:");
Array.ForEach(numbers, number =>
{
// This is a delegate (lambda expression) that processes each element
Console.WriteLine($"Element value: {number}");
});

// 5. Parallel iteration for large arrays (requires System.Threading.Tasks)
// Best for computationally intensive operations on large arrays
using System.Threading.Tasks;

Console.WriteLine("Using Parallel.ForEach:");
Parallel.ForEach(numbers, number =>
{
// This code runs in parallel for better performance on large arrays
// Be careful with shared resources in parallel code
Console.WriteLine($"Processing {number} on thread {Thread.CurrentThread.ManagedThreadId}");
});

2.5.3 - Multi-Dimensional and Jagged Arrays​

C# supports two types of multi-dimensional arrays: rectangular (true multi-dimensional arrays) and jagged (arrays of arrays). Each has its own use cases and performance characteristics.

2.5.3.1 - Multi-Dimensional Arrays​

Multi-dimensional or rectangular arrays are defined by multiple dimensions. They are particularly useful for representing matrices, grids, or any data that naturally fits into a table-like structure with fixed dimensions.

// Declaring and initializing a 2D array (4 rows, 2 columns)
// The first dimension represents rows, the second represents columns
int[,] grid = new int[4, 2];

// Initializing a 2D array with values
int[,] matrix = new int[,] {
{ 1, 2 }, // Row 0
{ 3, 4 }, // Row 1
{ 5, 6 }, // Row 2
{ 7, 8 } // Row 3
};

// Accessing elements: use comma-separated indices
int element = matrix[1, 0]; // Gets the element at row 1, column 0 (value: 3)

// Getting dimensions using GetLength()
// GetLength(0) returns the length of the first dimension (rows)
// GetLength(1) returns the length of the second dimension (columns)
int rows = matrix.GetLength(0); // 4
int columns = matrix.GetLength(1); // 2

// Iterating through a 2D array requires nested loops
for (int i = 0; i < matrix.GetLength(0); i++)
{
for (int j = 0; j < matrix.GetLength(1); j++)
{
// Process or modify each element
Console.Write($"{matrix[i, j]} ");

// Example: Set each element to the sum of its indices
matrix[i, j] = i + j;
}
Console.WriteLine(); // New line after each row
}

// You can also create 3D or higher dimensional arrays
// This creates a 2x3x4 array (2 "layers", each with 3 rows and 4 columns)
int[,,] cube = new int[2, 3, 4];

// Accessing elements in a 3D array
cube[0, 1, 2] = 42; // Sets the value at layer 0, row 1, column 2

2.5.3.2 - Jagged Arrays​

Jagged arrays are arrays of arrays, allowing each "row" to have a different length. They're useful when your data doesn't fit neatly into a rectangular structure, such as when each row needs a different number of columns.

// Declaring a jagged array with 3 rows (but no columns yet)
int[][] jaggedArray = new int[3][];

// Initialize each row with a different length
jaggedArray[0] = new int[2]; // First row has 2 columns
jaggedArray[1] = new int[5]; // Second row has 5 columns
jaggedArray[2] = new int[3]; // Third row has 3 columns

// Setting values
jaggedArray[0][0] = 1;
jaggedArray[0][1] = 2;

jaggedArray[1][0] = 10;
jaggedArray[1][1] = 20;
jaggedArray[1][2] = 30;
jaggedArray[1][3] = 40;
jaggedArray[1][4] = 50;

jaggedArray[2][0] = 100;
jaggedArray[2][1] = 200;
jaggedArray[2][2] = 300;

// A more concise way to initialize a jagged array
int[][] triangle = new int[][]
{
new int[] { 1 }, // Row 0: 1 element
new int[] { 2, 3 }, // Row 1: 2 elements
new int[] { 4, 5, 6 }, // Row 2: 3 elements
new int[] { 7, 8, 9, 10 } // Row 3: 4 elements
};

// Accessing elements: use separate brackets for each dimension
int value = triangle[2][1]; // Gets the element at row 2, column 1 (value: 5)

// Getting the length of each dimension
int numberOfRows = triangle.Length; // 4
int elementsInRow2 = triangle[2].Length; // 3

// Iterating through a jagged array
for (int i = 0; i < triangle.Length; i++)
{
// Note: We use triangle[i].Length for the inner loop bound
// since each row can have a different length
for (int j = 0; j < triangle[i].Length; j++)
{
Console.Write($"{triangle[i][j]} ");
}
Console.WriteLine(); // New line after each row
}

// You can even have jagged arrays of jagged arrays
int[][][] complex = new int[2][][];
complex[0] = new int[2][];
complex[0][0] = new int[] { 1, 2 };
complex[0][1] = new int[] { 3, 4, 5 };
// Accessing: complex[0][1][2] would be 5

2.5.3.3 - Choosing Between Multi-Dimensional and Jagged Arrays​

AspectMulti-Dimensional ArraysJagged Arrays
Memory LayoutContiguous block of memoryArray of references to separate arrays
Size FlexibilityAll dimensions have fixed sizesEach inner array can have different length
PerformanceGenerally faster for rectangular dataCan be more efficient for sparse data
SyntaxUses commas: array[i, j]Uses separate brackets: array[i][j]
Use CaseWhen data fits a regular grid patternWhen rows have varying lengths
Memory UsageMore efficient for dense dataMore efficient for sparse data

Choose the array type that best matches your data structure and access patterns.


2.5.4 - Advanced Array Techniques​

2.5.4.1 - System.Array Methods​

The System.Array class offers a rich set of static methods for creating, manipulating, and querying arrays. These methods are essential for performing operations such as sorting, searching, and modifying arrays without implementing custom algorithms.

  • Array.Sort(): Sorts an array in ascending order. It can also accept a custom comparer for sorting based on user-defined logic.

    int[] numbers = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 };
    Array.Sort(numbers);
    // numbers now becomes {1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9}
  • Array.Find(): Finds the first element that matches a specified condition. It is useful for quickly locating items without manually iterating through the array.

    string[] names = { "Steve", "Bill", "James", "Meryl" };
    string found = Array.Find(names, name => name.StartsWith("M"));
    // found = "Meryl"
  • Array.BinarySearch(): Searches for an element using a binary search algorithm, which is efficient for sorted arrays. It returns the index of the element if found; otherwise, it returns a negative number.

    int index = Array.BinarySearch(numbers, 5);
    // index returns the position of '5' in the sorted array
  • Array.Reverse(): Reverses the sequence of the elements in the entire one-dimensional array or in a portion of the array.

    Array.Reverse(numbers);
    // numbers array is reversed
  • Array.Copy(): Copies elements from one array to another. It's especially useful when you need to extract a subset of an array or duplicate arrays without referencing the original array.

int[] sourceArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] destinationArray = new int[5]; // Allocate space for 5 elements
Array.Copy(sourceArray, 2, destinationArray, 0, 5);
// Copies 5 elements starting from index 2 of sourceArray to destinationArray starting at index 0
// destinationArray now contains {3, 4, 5, 6, 7}
  • Array.Resize<T>(): Dynamically changes the size of a one-dimensional array. It is particularly useful in scenarios where the array size needs to be adjusted dynamically during runtime.
int[] dynamicArray = { 1, 2, 3, 4, 5 };
Array.Resize(ref dynamicArray, 10);
// dynamicArray is now resized to hold 10 elements, with new elements initialized to 0
// dynamicArray after resize: {1, 2, 3, 4, 5, 0, 0, 0, 0, 0}

Note: In C#, the ref keyword is used to pass an argument to a function by reference instead of by value. This is important to understand because it affects how changes to the parameter are reflected outside of the function. The ref keyword is powerful, enabling direct manipulation of variables across different scopes within your application. However, it should be used judiciously, as it can lead to code that is harder to understand and maintain, especially in larger projects with many moving parts. It’s also critical to ensure that methods using ref parameters are clearly documented to avoid unintended side effects.

Summary Table of Common Array Methods:

MethodDescriptionExample
Array.Sort()Sorts an array in ascending order.Array.Sort(array);
Array.Find()Finds an element that matches a condition.Array.Find(array, condition);
Array.BinarySearch()Searches for an element using binary search.Array.BinarySearch(array, value);
Array.Reverse()Reverses the sequence of the elements in an array.Array.Reverse(array);
Array.Copy()Copies elements from one array to another.Array.Copy(sourceArray, destinationArray, length);
Array.Resize<T>()Changes the size of an array.Array.Resize(ref array, newSize);

For more information and a comprehensive list of all array methods available in the System.Array class, visit the official Microsoft documentation.

2.5.4.2 - Memory Efficiency​

Understanding the memory layout of arrays is crucial for developing high-performance applications. Value-type arrays store data directly in the array, while reference-type arrays store references to the actual data objects.

2.5.4.3 - Use Cases​

  • Sorting and Searching: Essential for managing ordered data.
  • Matrix Operations: Common in scientific calculations and graphics programming.
  • Data Buffers: Frequently used in I/O operations to store data temporarily.

Arrays are a foundational concept in C#, and mastering their use is a key skill for any developer. This chapter covers the essentials and advanced topics necessary to leverage arrays effectively in any C# application.