A multidimensional array is a single grid — rows and columns baked into one rectangular block, addressed with one index per dimension.
Picture a spreadsheet, or a seating chart for a theater: rows and columns, every row exactly the same width. You don't think of it as "a list of lists" — you think of it as one grid, and you find a seat by saying "row 3, seat 5." In the previous lesson you learned that an array is a single row of boxes. This lesson extends that same idea into two (or more) dimensions.
In this lesson, you'll learn what a multidimensional array is, how C#'s rectangular arrays work, how they're laid out in memory, and when a grid-shaped array is exactly the tool you need.
A multidimensional array is a grid. Instead of one index to find a value, you use two (for a grid), three (for a cube), or more. A 2D array is the most common — think rows and columns, like a spreadsheet or a chessboard.
In C#, this specific kind — where every row has the same length — is called a rectangular array, written with commas inside the brackets: T[,] for 2D, T[,,] for 3D, and so on. It's a single array object with multiple dimensions, not an array containing other arrays (that's a different structure — a jagged array, covered in the next lesson).
grid[row, column], one index per dimension, separated by commas.Some data is naturally grid-shaped: a spreadsheet, a tic-tac-toe board, a seating chart, pixel data in an image, a multiplication table. You could try to force this into a single 1D array — for a 3×3 board, you'd flatten it into 9 values and do the row/column math yourself every time:
int[] board = new int[9]; // flattened 3x3 board
int row = 1, col = 2;
int flatIndex = row * 3 + col; // you compute this yourself, every time
board[flatIndex] = 5;This works, but it's error-prone (get the multiplier wrong and everything shifts) and it hides the true shape of the data from anyone reading the code.
A rectangular array lets the language do that row/column math for you, and lets your code say what it means:
int[,] board = new int[3, 3];
board[1, 2] = 5; // row 1, column 2 — reads exactly like "row 1, col 2"grid[1, 2] — row 1, column 2. Every row has exactly 4 columns — that's what makes it "rectangular."
int[,] grid = new int[3, 4]; // 3 rows, 4 columns, all default to 0
[,] is what marks this as a 2D rectangular array, not two separate arrays.int[,] grid =
{
{ 1, 2, 3 },
{ 4, 5, 6 },
}; // 2 rows, 3 columns
{ } group is one row — and every row must have the same number of values.int value = grid[1, 2]; // read row 1, column 2 → 6
grid[0, 0] = 99; // write row 0, column 0
int rows = grid.GetLength(0); // 2
int cols = grid.GetLength(1); // 3
for (int r = 0; r < rows; r++)
{
for (int c = 0; c < cols; c++)
Console.Write($"{grid[r, c]} ");
Console.WriteLine();
}
GetLength(0) is the row count, GetLength(1) is the column count — a plain .Length would instead give the total cell count (6, in this example), which is rarely what you want.for loops — outer for rows, inner for columns — are the standard way to walk a grid.// A 3x3 tic-tac-toe board: 0 = empty, 1 = X, 2 = O
int[,] board = new int[3, 3];
board[0, 0] = 1; // X
board[1, 1] = 2; // O
board[2, 2] = 1; // X
for (int row = 0; row < board.GetLength(0); row++)
{
for (int col = 0; col < board.GetLength(1); col++)
{
char symbol = board[row, col] switch
{
1 => 'X',
2 => 'O',
_ => '.'
};
Console.Write($"{symbol} ");
}
Console.WriteLine();
}
// X . .
// . O .
// . . XCode → Meaning → Result:
board[row, col] maps directly onto how a person would describe a board position — "row 1, column 1."A small movie theater sells seats laid out in a fixed grid: 6 rows, 8 seats per row. Every row has exactly the same width, which makes this a perfect rectangular array — not every seating chart is this uniform (see the jagged array lesson next for when it isn't).
const int Rows = 6;
const int SeatsPerRow = 8;
bool[,] seatTaken = new bool[Rows, SeatsPerRow]; // all false = all available
void BookSeat(int row, int seat)
{
if (row < 0 || row >= Rows || seat < 0 || seat >= SeatsPerRow)
{
Console.WriteLine("Invalid seat.");
return;
}
if (seatTaken[row, seat])
{
Console.WriteLine($"Row {row + 1}, Seat {seat + 1} is already taken.");
return;
}
seatTaken[row, seat] = true;
Console.WriteLine($"Booked Row {row + 1}, Seat {seat + 1}.");
}
void PrintChart()
{
for (int r = 0; r < Rows; r++)
{
for (int s = 0; s < SeatsPerRow; s++)
Console.Write(seatTaken[r, s] ? "[X]" : "[ ]");
Console.WriteLine($" Row {r + 1}");
}
}
BookSeat(2, 4);
BookSeat(2, 4); // already taken
PrintChart();
// Booked Row 3, Seat 5.
// Row 3, Seat 5 is already taken.
// [ ][ ][ ][ ][ ][ ][ ][ ] Row 1
// [ ][ ][ ][ ][ ][ ][ ][ ] Row 2
// [ ][ ][ ][ ][X][ ][ ][ ] Row 3
// [ ][ ][ ][ ][ ][ ][ ][ ] Row 4
// [ ][ ][ ][ ][ ][ ][ ][ ] Row 5
// [ ][ ][ ][ ][ ][ ][ ][ ] Row 6A rectangular array is like a city built on a perfect grid — every street the same length, every block the same size. You find any building with two numbers: "5th Avenue and 3rd Street." You don't need to know how the city was constructed — the grid structure itself tells you exactly where to look.
A rectangular array works the same way: grid[row, column] is your "avenue and street" — two coordinates that pinpoint one cell in a perfectly uniform layout.
grid[row, col] becomes something like flatIndex = row * columnCount + col.IndexOutOfRangeException, just like a 1D array.int[,] vs int[][] — these are not the same typeint[,] (comma) is one rectangular array. int[][] (separate brackets) is an array of arrays — a jagged array, where each inner array can be a different length. The next lesson covers jagged arrays in depth; for now, just remember: comma = one uniform grid, separate brackets = a collection of independent rows.
.Length vs .GetLength(dimension)On a multidimensional array, .Length gives you the total number of cells (rows × columns), not the row or column count individually. To get a specific dimension's size, use .GetLength(0) for rows, .GetLength(1) for columns.
It's easy to accidentally swap them, especially with non-square grids:
int[,] grid = new int[3, 8]; // 3 rows, 8 columns
grid[5, 1] = 10; // IndexOutOfRangeException — there is no row 5, only rows 0-2Always double-check which dimension is rows and which is columns for your specific grid, and stay consistent throughout your code.
foreach when you need row/column position foreach visits every cell, but doesn't tell you which row and column you're on:
foreach (int cell in grid)
Console.Write(cell); // you get the value, but not its [row, col] position Use nested for loops when the position matters, as shown in "How It Works" above.
.Length gives the row count grid.Length on a 3×4 grid returns 12 (total cells), not 3. Use grid.GetLength(0) for rows.
List<List<T>> or a purpose-built type.Dictionary<TKey, TValue>.int[,] = one rectangular array (comma inside the brackets)grid[row, col] = your two coordinates into the gridGetLength(0)/GetLength(1) for row/column counts, not .Length.
T[,]) is a single grid — one array object with two (or more) dimensions.grid[row, col]; check its shape with GetLength(0) and GetLength(1), not .Length.You've seen how rectangular arrays model grid-shaped data with one uniform block of memory. Let's test it.
1. Which declaration creates a genuine 2D rectangular array?
Correct: B
Why B is correct: The comma inside the square brackets, [,], is C#'s syntax for a rectangular array — one array object with two dimensions.
Why A is incorrect: Separate brackets, [][], declare a jagged array — an array of arrays, which is a different structure covered in the next lesson.
Why C is incorrect: List<int> is a one-dimensional resizable list, not a grid.
Why D is incorrect: That syntax isn't valid C# — the type and brackets must appear together, as in int[,].
Reinforcement: A comma inside one set of brackets means one rectangular array; separate bracket pairs mean an array of arrays.
2. Given int[,] grid = new int[3, 5];, what does grid.Length return?
Correct: C
Why C is correct: .Length on a multidimensional array returns the total number of elements across all dimensions — here, 3 rows × 5 columns = 15 cells.
Why A is incorrect: 3 is the row count, which you'd get from GetLength(0), not .Length.
Why B is incorrect: 5 is the column count, which you'd get from GetLength(1), not .Length.
Why D is incorrect: .Length works fine on multidimensional arrays — it just answers a different question (total cells) than beginners often expect.
Reinforcement: Use GetLength(0)/GetLength(1) for individual dimension sizes; use .Length only when you want the grand total.
3. Why is a rectangular array a good fit for a fixed 6-row, 8-seat theater seating chart, but not for an org chart where each manager has a different number of direct reports?
Correct: B
Why B is correct: Rectangular arrays enforce a uniform shape — every row must be the same length. The theater's 8-seats-per-row layout satisfies that. An org chart's manager-to-reports structure does not, since team sizes vary — that irregular shape calls for a jagged array instead.
Why A is incorrect: Rectangular arrays work fine with bool, as shown in the seating chart example.
Why C is incorrect: Rectangular arrays are typically faster and more memory-compact than jagged arrays for uniform data — the issue here is shape, not speed.
Why D is incorrect: The structures genuinely differ — forcing an irregular org chart into a rectangular array would waste memory on unused "seats" or make illegal states representable.
Reinforcement: Choose a rectangular array only when every row is genuinely the same length.
4. What is the most likely cause of an IndexOutOfRangeException when working with int[,] grid = new int[4, 6];?
Correct: C
Why C is correct: With 4 rows, valid row indexes are 0–3. Index 4 is out of range — the same off-by-one mistake that trips people up with 1D arrays, just on a second dimension too.
Why A is incorrect: [3, 5] is valid — row 3 is the last row (0–3), and column 5 is the last column (0–5).
Why B is incorrect: [0, 0] is always a valid position in any non-empty grid.
Why D is incorrect: GetLength(0) simply returns a number (4); it doesn't touch grid data and can't throw an out-of-range exception.
Reinforcement: With N rows, valid row indexes always run 0 through N-1 — the same rule as 1D arrays, applied per dimension.
You can now model true grid-shaped data with rectangular arrays. Next: what to do when the rows aren't all the same length.
dotnetmadeeasy.com — Learn C# and .NET, the right way.