2D array. A two-dimensional array is used to store elements that are indexed by two coordinates. In C# we separate the indexes with a comma. We can create 2D arrays of any element type.
We can initialize 2D arrays with a single statement—all the memory is part of a single region. Also remember that jagged arrays can represent a 2D space.
First example. Here we show a 2-dimensional string array. The program creates a 2x2 string array and then prints out all 4 elements with Console.WriteLine.
Part 1 Here we initialize the 2D array with an initializer expression. Each element in this array is a string.
Part 2 We use the indexing syntax to access elements. We use 2 indexes for a 2D array. Each dimension is indexed starting at zero.
Tip It makes no difference whether we think of the first index as a row or column, or the other way around.
using System;
// Part 1: create 2D array of strings.string[,] array = new string[,]
{
{ "cat", "dog" },
{ "bird", "fish" },
};
// Part 2: access (and print) values.
Console.WriteLine(array[0, 0]);
Console.WriteLine(array[0, 1]);
Console.WriteLine(array[1, 0]);
Console.WriteLine(array[1, 1]);cat
dog
bird
fish
Rank. Every array has a rank: this is the number of dimensions in the array. A one-dimensional array has a rank of 1. We access the Rank property from the Array base class.
Here We design a method (Handle) that receives an array reference. It then tests the Rank of the parameter array.
And In Handle() we test both 1D and 2D arrays in the same method. This method uses GetValue to access the array elements.
using System;
class Program
{
static void Main()
{
// ... A one-dimensional array.int[] one = new int[2];
one[0] = 1;
one[1] = 2;
Handle(one);
// ... A two-dimensional array.int[,] two = new int[2, 2];
two[0, 0] = 0;
two[1, 0] = 1;
two[0, 1] = 2;
two[1, 1] = 3;
Handle(two);
}
static void Handle(Array array)
{
Console.WriteLine("Rank: " + array.Rank);
switch (array.Rank)
{
case 1:
for (int i = 0; i < array.Length; i++)
{
Console.WriteLine(array.GetValue(i));
}
break;
case 2:
for (int i = 0; i < array.GetLength(0); i++)
{
for (int x = 0; x < array.GetLength(1); x++)
{
Console.Write(array.GetValue(i, x));
}
Console.WriteLine();
}
break;
}
}
}Rank: 1
12
Rank: 2
0213
GetUpperBound loop. GetUpperBound() receives the highest index of the specified rank. It returns an int. The word rank is the same concept as "dimension."
Tip A 2D array has 2 ranks, 0 and 1. On more complex arrays, GetUpperBound will work more reliably than other approaches.
using System;
string[,] codes = new string[,]
{
{ "AA", "BB" },
{ "CC", "DD" }
};
// Get the upper bound.// ... Use for-loop over rows.
for (int i = 0; i <= codes.GetUpperBound(0); i++)
{
string s1 = codes[i, 0];
string s2 = codes[i, 1];
Console.WriteLine("{0}, {1}", s1, s2);
}AA, BB
CC, DD
Length-based loop. The fastest method for a 2D array is to do some arithmetic. In this example, there are five rows. GetUpperBound(0) will return 4.
And If we take Length, which is 10, and divide by 2, we get 5. We can iterate until we reach 5.
using System;
string[,] words = new string[,]
{
{ "ONE", "TWO" },
{ "THREE", "FOUR" },
{ "FIVE", "SIX" }
};
// Loop based on length.// ... Assumes each subarray is two elements long.
for (int i = 0; i < words.Length / 2; i++)
{
string s1 = words[i, 0];
string s2 = words[i, 1];
Console.WriteLine("{0}, {1}", s1, s2);
}ONE, TWO
THREE, FOUR
FIVE, SIX
GetUpperBound, int example. Here we get the 2 dimensions of the array and iterate through them. We cache array bounds in locals for better performance and clarity.
Info The performance of GetUpperBound can make an impact. But often, using jagged arrays (or flattened arrays) is the best solution.
Tip Var is used to reference the 2D array. This makes the syntax for the program simpler—var is often used with 2D arrays.
using System;
var codes = new int[,]
{
{ 200, 400 },
{ 2000, 4000 },
{ 20000, 40000 }
};
// Get all bounds before looping.
int bound0 = codes.GetUpperBound(0);
int bound1 = codes.GetUpperBound(1);
// ... Loop over bounds.for (int i = 0; i <= bound0; i++)
{
for (int x = 0; x <= bound1; x++)
{
// Display the element at these indexes.
Console.WriteLine("ELEMENT: {0}", codes[i, x]);
}
Console.WriteLine("--");
}ELEMENT: 200
ELEMENT: 400
--
ELEMENT: 2000
ELEMENT: 4000
--
ELEMENT: 20000
ELEMENT: 40000
--
No initializers. We can create an empty 2D array by specifying its dimensions. All elements have the default value (for ints this is 0).
Also We can use a 2D array reference to refer to any size of array, but the element type must match.
using System;
// A two-dimensional array reference.int[,] array = new int[2, 2];
array[0, 0] = 1;
Console.WriteLine(array[0, 0]);
// The same reference can hold a different size of array.
array = new int[3, 3];
array[2, 2] = 1;
Console.WriteLine(array[2, 2]);1
1
Arguments. A method may receive a 2D array by specifying the type of the elements. The dimensions are not used in the argument list—any 2D array of the correct element may be passed.
Tip Changes to elements in the argument will also affect the original version. Only the reference is copied to the new method.
Here The PrintFirstElement() method receives a 2D bool array. It then prints the value of the bool in the first row and first column.
using System;
class Program
{
static void PrintFirstElement(bool[,] values)
{
// Display value of first element in first row.
Console.WriteLine(values[0, 0]);
}
static void Main()
{
// Any array size of the right element type can be used.bool[,] values = new bool[100, 100];
values[0, 0] = true;
PrintFirstElement(values);
}
}True
Loops. 2D array loops are complicated. It is easy to cause errors related to invalid indexes. Here our 2D array is a four-element box, composed of two pairs.
Next To begin our for-loop, we acquire the upper bound of the zero dimension, and the upper bound of the first dimension of the array.
Warning The loop will not continue to work correctly if the array reference itself is modified or the array data is resized.
Note Nested loops are not always needed. In an array with 2 elements in each row, you can access positions 0 and 1 from a single for-loop.
using System;
// Instantiate a new 2D string array.string[,] array = new string[2, 2];
array[0, 0] = "top left";
array[0, 1] = "top right";
array[1, 0] = "bottom left";
array[1, 1] = "bottom right";
// Get upper bounds for the array
int bound0 = array.GetUpperBound(0);
int bound1 = array.GetUpperBound(1);
// Use for-loops to iterate over the array elements.
for (int variable1 = 0; variable1 <= bound0; variable1++)
{
for (int variable2 = 0; variable2 <= bound1; variable2++)
{
string value = array[variable1, variable2];
Console.WriteLine(value);
}
Console.WriteLine();
}top left
top right
bottom left
bottom right
Add row, add column. You cannot add a new row or column to a 2D array—the array is fixed in size and a new array must be created to add elements. Here we introduce AddRow and AddColumn.
Info AddRow allocates a new 2D int array and copies the current array into the new one. It then copies a separate int array as a new row.
Next AddColumn allocates a new array with an extra column and copies the previous array. It copies an int array into a new column.
Note These methods do not "add rows or columns" to a 2D array. Instead they create a new array and add to that.
Tip For optimal performance, consider using a List and adding int arrays to that. Or add Lists of ints to a List in a 2D list.
using System;
class Program
{
static int[,] AddRow(int[,] original, int[] added)
{
int lastRow = original.GetUpperBound(0);
int lastColumn = original.GetUpperBound(1);
// Create new array.int[,] result = new int[lastRow + 2, lastColumn + 1];
// Copy existing array into the new array.
for (int i = 0; i <= lastRow; i++)
{
for (int x = 0; x <= lastColumn; x++)
{
result[i, x] = original[i, x];
}
}
// Add the new row.
for (int i = 0; i < added.Length; i++)
{
result[lastRow + 1, i] = added[i];
}
return result;
}
static int[,] AddColumn(int[,] original, int[] added)
{
int lastRow = original.GetUpperBound(0);
int lastColumn = original.GetUpperBound(1);
// Create new array.int[,] result = new int[lastRow + 1, lastColumn + 2];
// Copy the array.
for (int i = 0; i <= lastRow; i++)
{
for (int x = 0; x <= lastColumn; x++)
{
result[i, x] = original[i, x];
}
}
// Add the new column.
for (int i = 0; i < added.Length; i++)
{
result[i, lastColumn + 1] = added[i];
}
return result;
}
static void Display(int[,] array)
{
// Loop over 2D int array and display it.
for (int i = 0; i <= array.GetUpperBound(0); i++)
{
for (int x = 0; x <= array.GetUpperBound(1); x++)
{
Console.Write(array[i, x]);
Console.Write(" ");
}
Console.WriteLine();
}
}
static void Main()
{
int[,] values = { { 10, 20 }, { 30, 40 } };
Console.WriteLine("CURRENT");
Display(values);
// Add row and display the new array.int[,] valuesRowAdded = AddRow(values, new int[] { 50, 60 });
Console.WriteLine("ADD ROW");
Display(valuesRowAdded);
// Add column and display the new array.int[,] valuesColumnAdded = AddColumn(valuesRowAdded, new int[] { -1, -2, -3 });
Console.WriteLine("ADD COLUMN");
Display(valuesColumnAdded);
}
}CURRENT
10 20
30 40
ADD ROW
10 20
30 40
50 60
ADD COLUMN
10 20 -1
30 40 -2
50 60 -3
Error, cannot convert. A 2D array is not compatible with a 1D array—the types are different. So we cannot pass a 2D array to a method that accepts a 1D array (or the opposite case).
class Program
{
static void Main()
{
string[,] array = new string[10, 10];
Test(array);
}
static void Test(string[] example)
{
}
}error CS1503: Argument 1: cannot convert from 'string[*,*]' to 'string[]'
Benchmark, GetUpperBound. GetUpperBound is slow. Its result should be stored in a local variable. This benchmark shows the performance decrease with GetUpperBound.
Version 1 This code uses GetUpperBound 0 to loop over the rows in a 2D array. It stores the result of GetUpperBound in a local.
Version 2 This version uses the fact that each row has 2 elements, so it can derive the total row count from the Length divided by 2.
Result On .NET 5 in 2021, using the Length property for a loop boundary is faster than using GetUpperBound.
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
const int _max = 10000000;
int[,] weights = new int[,]
{
{ 100, 20 },
{ 0, 0 },
{ 5, 10 },
};
// Version 1: sum 2D array with GetUpperBound loop.
var s1 = Stopwatch.StartNew();
for (int i = 0; i < _max; i++)
{
int sum = 0;
int top = weights.GetUpperBound(0);
for (int z = 0; z <= top; z++)
{
sum += weights[z, 0] + weights[z, 1];
}
if (sum != 135)
{
return;
}
}
s1.Stop();
// Version 2: sum 2D array with Length-based loop.
var s2 = Stopwatch.StartNew();
for (int i = 0; i < _max; i++)
{
int sum = 0;
int count = weights.Length / 2;
for (int z = 0; z < count; z++)
{
sum += weights[z, 0] + weights[z, 1];
}
if (sum != 135)
{
return;
}
}
s2.Stop();
Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));
Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));9.34 ns GetUpperBound
6.98 ns Length / 2
Dimensions. In C# we can also specify arrays with more than two dimensions. We can use another comma in the indexing syntax. It will work as expected.
Flatten array. A 2D array can be flattened into a 1D array. This can yield some performance improvements, but it makes a program's syntax more complex.
Lists. You can use the generic type List to simulate a jagged or 2D List that is dynamically resizable. The syntax for this nested type is somewhat more confusing.
Tip A List can solve problems that would otherwise require confusing 2D array resizing and copying.
With initializer syntax, we can create 2D arrays. And then with nested for-loops we can iterate over them—it is easiest to use Length, but GetUpperBound is sometimes needed.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Jun 27, 2023 (rewrite).