Back to Blog
C#

C# Multidimensional Array: Syntax and Usage

c# multidimensional array: Learn how to declare, initialize, and iterate multidimensional arrays in C#, including rectangular vs jagged arrays and performance tradeoffs.

C#arraysjagged arraysdata structures.NET
Diagram showing a two-dimensional C# array as a grid of cells with rows and columns visually distinct and index labels on the edges

A C# multidimensional array is a single array object with multiple dimensions, declared with commas inside the type specification. The most common form is a two-dimensional array, but C# supports any number of dimensions.

int[,] grid = new int[3, 4];

This declares a 3-by-4 rectangular array of integers. The comma in int[,] indicates two dimensions. The array is rectangular, meaning every row has the same length. That constraint is central to how multidimensional arrays behave and where they differ from jagged arrays.

Declaring and Initializing Multidimensional Arrays

You can declare a multidimensional array without immediately initializing it:

double[,] matrix; matrix = new double[2, 3];

The new expression allocates the storage and zero-initializes every element. For numeric types, each element defaults to zero; for reference types, each element defaults to null.

Initialization can also happen inline with values:

int[,] board = new int[,] { { 1, 2, 3 }, { 4, 5, 6 } };

The compiler infers the dimension sizes from the nested initializers. You can omit the size in the new expression when providing initializers, but you cannot omit the comma count. The number of commas inside the brackets determines the rank, and every nested initializer must have the same number of elements.

For three dimensions:

int[,,] cube = new int[2, 3, 4];

Each dimension is specified in the size expression. The same pattern extends to four or more dimensions, though anything beyond three becomes difficult to read and is rarely justified.

Accessing Elements

Elements are accessed using comma-separated indices:

int value = board[1, 2]; // 6 board[0, 0] = 10;

Indices are zero-based. Accessing an out-of-range index throws IndexOutOfRangeException, so bounds must be checked when the indices come from user input or runtime data.

The Length property returns the total number of elements across all dimensions, not the size of any single dimension. To get the size of a specific dimension, use GetLength:

int rows = board.GetLength(0); int cols = board.GetLength(1);

GetLength(0) returns the first dimension, which is conventionally the row count. This distinction is a frequent source of off-by-one errors when developers assume Length gives the row count.

Iterating Over a Multidimensional Array

Nested loops are the standard way to iterate over a multidimensional array:

for (int i = 0; i < board.GetLength(0); i++) { for (int j = 0; j < board.GetLength(1); j++) { Console.Write(board[i, j]); } }

Using GetLength in the loop conditions keeps the code correct even if the array size changes later. Hard-coding a size in the loop condition is a common maintenance hazard.

A foreach loop also works and visits elements in row-major order:

foreach (int value in board) { Console.WriteLine(value); }

However, foreach does not expose the indices, so it is only useful when you need the values alone. If you need to know which row or column an element came from, you must use indexed loops.

Rectangular vs Jagged Arrays

A jagged array is an array of arrays, declared with separate bracket pairs:

int[][] jagged = new int[3][]; jagged[0] = new int[2]; jagged[1] = new int[5]; jagged[2] = new int[3];

Each row can have a different length. This is the key difference from a multidimensional array, where every row has the same length. The choice between the two affects syntax, memory layout, and performance.

FeatureRectangular arrayJagged array
Declarationint[,]int[][]
Row lengthFixedVariable
MemorySingle contiguous blockMultiple array objects
Element accessarr[i, j]arr[i][j]
Cache localityGood when iterating in orderDepends on row allocation

Jagged arrays are often faster in practice because each row is a separate one-dimensional array, and accessing arr[i] returns a direct reference that the JIT can optimize. Rectangular arrays require computing an offset from multiple indices before each access.

Performance and Memory Considerations

A rectangular array is allocated as one contiguous block of memory. This gives good cache locality when iterating in row-major order, because consecutive elements are adjacent in memory. The tradeoff is that multi-dimensional indexing requires a computed offset and bounds checks on every access.

A jagged array involves multiple heap allocations: one for the outer array and one for each row. This increases allocation pressure but often improves access speed because each row is a simple one-dimensional array with a single index calculation.

For performance-sensitive code, a single-dimensional array with manual index math is sometimes the best option:

int[] flat = new int[rows * cols]; int value = flat[i * cols + j];

This avoids the overhead of multi-dimensional indexing entirely and gives the JIT the simplest possible access pattern. The cost is manual bounds reasoning and less readable code.

No benchmark numbers are presented here because the relative performance depends on the runtime, the access pattern, and the array size. The mechanism, however, is consistent: fewer index calculations and better locality generally win for tight loops.

Common Mistakes and Edge Cases

One common mistake is confusing Length with the row count. Length returns the total element count, so using it as a loop bound for a single dimension will miss elements or throw an exception.

Another is using a multidimensional array when a jagged array is more appropriate. If rows have different lengths, you must use a jagged array; a rectangular array cannot represent that shape.

Initializing with mismatched nested initializers causes a compile error:

int[,] bad = new int[,] { { 1, 2 }, { 3, 4, 5 } // compile error: inconsistent column count };

The compiler enforces rectangular shape at compile time, which is a useful safety guarantee but also a limitation.

Empty dimensions are allowed: new int[0, 5] is valid and has a Length of zero. Code that iterates over such an array must handle the zero-length case gracefully, especially when computing row or column counts.

When to Choose Alternatives

For dynamic data where the size changes after creation, a List<T> or List<List<T>> is more appropriate. Multidimensional arrays have a fixed size after allocation and cannot be resized.

For sparse data, a dictionary keyed by a coordinate tuple is often clearer and more memory-efficient:

Dictionary<(int, int), int> sparse = new(); sparse[(2, 3)] = 42;

This avoids allocating memory for empty cells, which matters when the array is large but most positions are unused.

For matrix-heavy numerical work, consider a library that provides vectorized operations. Raw C# arrays do not perform SIMD-accelerated matrix math automatically, so a dedicated math library will usually be faster for large dense matrices. The choice between a rectangular array and a jagged array should be driven by the shape of the data and the access pattern, not by habit.

c# multidimensional array: Practical Usage and Code Examples | RYUSLOG DEV