An array in C# is a region of memory that stores a certain number of elements—each element has the same type. Arrays are harder to use than Lists, but can be more efficient.
Arrays are inside many things—in this sense, they are a foundation that we use to build more complex types. Sometimes they are required as arguments, or returned from methods.
String
arraysWe begin with string
arrays. We use square brackets to set array sizes and access elements, and the Length
property returns the element count.
string
array of 3 elements, and then assign strings to the array indexes (starting at 0).string
array is created with an array initializer expression. It is shorter and easier to type.using System; // Version 1: create empty string array. // ... Assign into the array. string[] animals = new string[3]; animals[0] = "deer"; animals[1] = "moose"; animals[2] = "boars"; Console.WriteLine("ARRAY 1: " + animals.Length); // Version 2: use array initializer. string[] animals2 = new string[] { "deer", "moose", "boars" }; Console.WriteLine("ARRAY 2: " + animals2.Length); // Version 3: minimal array initializers. string[] animals3 = { "deer", "moose", "boars" }; string[] animals4 = ["deer", "moose", "boars"]; string[] animals5 = new[] { "deer", "moose", "boars" }; Console.WriteLine($"{animals3.Length} {animals4.Length} {animals5.Length}");ARRAY 1: 3 ARRAY 2: 3 3 3 3
The first element is at index 0. And the last element is always at the array's length minus one. These elements can be accessed by using the indexer syntax.
int
array and populate it with a for
-loop. Then we access element 0 and the last element.using System; // Create an array. int[] array = new int[4]; // Populate the array. int value = 10; for (int i = 0; i < array.Length; i++) { array[i] = value++ * 10; } // Access first and last elements. int first = array[0]; int last = array[array.Length - 1]; Console.WriteLine($"FIRST: {first}"); Console.WriteLine($"LAST: {last}");FIRST: 100 LAST: 130
With this loop, no indexes are needed—the loop itself handles the indexes. For many programs, a foreach
-loop is the clearest loop. We use string
interpolation to display the colors.
string[] array = { "red", "blue", "green" }; // Loop with foreach and write colors with string interpolation. foreach (string color in array) { System.Console.WriteLine($"Color = {color}"); }Color = red Color = blue Color = green
For
-loopWith this loop we use indexes. We have a start, and end, and increment expression. Here we use a for
-loop to access and print each element in a string
array.
using System; string[] array = ["Socrates", "Plato"]; // Use for-loop on array. for (int i = 0; i < array.Length; i++) { // Get element, and print index and element value. string element = array[i]; Console.WriteLine("INDEX: {0}, VALUE: {1}", i, element); }INDEX: 0, VALUE: Socrates INDEX: 1, VALUE: Plato
Index
from endSuppose we want to access the last element in an array. The simplest syntax to do this is to use the "^1" notation—this means "last."
using System; int[] values = {10, 20, 30}; // Use indexes from the end with caret. int last = values[^1]; int middle = values[^2]; int first = values[^3]; Console.WriteLine($"{first} {middle} {last}");10 20 30
Int
array, parameterIn structural programming we pass arrays as arguments to methods. The entire contents of the array are not copied—just the small reference.
int
array of 3 integer elements—these are 3 negative integers.int
array. It accesses the array and returns a value based on the first element.using System; class Program { static void Main() { // Step 1: create 3-element array. int[] array = { -5, -6, -7 }; // Step 2: pass array reference to method. int result = MultiplyFirstElement(array); Console.WriteLine("FIRST ELEMENT MULTIPLIED: {0}", result); } static int MultiplyFirstElement(int[] array) { // Step 3: multiply the first element by 2 and return it. return array[0] * 2; } }-10
We can return arrays from methods—a method might need to generate data for the array beforehand. In this program, we create an array of 3 strings and return it in GetTreeNameArray
.
using System; class Program { static void Main() { // Get tree array, and merge its results into a single string. string result = string.Join("; ", GetTreeNameArray()); // Write the tree names. Console.WriteLine("TREES: {0}", result); } static string[] GetTreeNameArray() { // Put 3 tree names in the array and return it. string[] array = { "Elm", "Oak", "Palm" }; return array; } }TREES: Elm; Oak; Palm
Here is another syntax example. To create an empty string
array, we can use an empty initializer expression. Or we can specify a 0 length.
using System; // Create string array with no elements. var empty1 = new string[] { }; Console.WriteLine(empty1.Length == 0); // This syntax has the same result. var empty2 = new string[0]; Console.WriteLine(empty2.Length == 0);True True
IndexOf
Sometimes we want to search for a value in an array without using a for
-loop. Consider the Array.IndexOf
method. We can use this to search an array by value.
Array.IndexOf
is the array we are trying to search.string
"dog."IndexOf
methods return -1 when no element is found. This value often must be checked in an if
-statement.using System; string[] array = { "cat", "dog", "bird", "fish" }; // The dog string is at index 1. int dogIndex = Array.IndexOf(array, "dog"); Console.WriteLine(dogIndex); // There is no monkey string in the array. // ... So IndexOf returns -1. int monkeyIndex = Array.IndexOf(array, "monkey"); Console.WriteLine(monkeyIndex);1 -1
Class
, indexerWe use arrays as fields (or properties) in classes. This is useful for storing values. In the Test class
here, we have a string
array field.
class
is a property accessor. It provides a clean way for external code to access the internal array.class
is called an Indexer. An indexer uses the this
-keyword.class Program { static void Main() { // Create new instance with string array. Test test = new Test(); // Loop over elements with property. foreach (string element in test.Elements) { System.Console.WriteLine(element); } // Get first string element. System.Console.WriteLine(test[0]); } } public class Test { string[] _elements = { "one", "two", "three" }; public string[] Elements { get { return _elements; } } public string this[int index] { get { return _elements[index]; } } }one two three one
string
arraysIt is possible to use built-in methods like Join
and Split
to convert a string
array into a string
, and back again. We can also use loops and StringBuilder
.
Join
method to combine the 3 string
literals within the "elements" array.Split
to change our joined string
back into a string
array. The 2 string
arrays are separate in memory.using System; string[] elements = ["cat", "dog", "fish"]; Console.WriteLine(elements[0]); // Part 1: join strings into a single string. string joined = string.Join("|", elements); Console.WriteLine(joined); // Part 2: separate joined strings with Split. string[] separated = joined.Split('|'); Console.WriteLine(separated[0]);cat cat|dog|fish cat
String
argsWhen a C# program is started, an optional string
array is received from the operating system. This array, args, contains string
arguments.
using System; class Program { static void Main(string[] args) { // ... Loop over arguments passed to this program. foreach (string value in args) { Console.WriteLine("Argument: {0}", value); } } }Argument: hello Argument: world
ref
, array elementWith the ref
keyword, we can return a reference to an array element. Here we have an int
array. FirstElement
returns a ref
to the element at index 0.
FirstElement
to modify the array. The "codes" array is modified.class Program { static ref int FirstElement(int[] array) { // Return ref to first element in array. return ref array[0]; } static void Main() { int[] codes = { 10, 20, 30 }; // Change first element to a new value. FirstElement(codes) = 60; // Display modified array. for (int i = 0; i < codes.Length; i++) { System.Console.WriteLine(codes[i]); } } }60 20 30
Random
arrayWe can use the Random
NextBytes
method to fill a byte
array with random values. This does not require much custom code.
Random
object, and then call NextBytes
to fill up the array.using System; // Part 1: fill up a random byte array. byte[] array = new byte[4]; Random random = new Random(); random.NextBytes(array); // Part 2: display random array elements. foreach (byte value in array) { Console.WriteLine("RANDOM ELEMENT: {0}", value); }RANDOM ELEMENT: 240 RANDOM ELEMENT: 197 RANDOM ELEMENT: 121 RANDOM ELEMENT: 218
IEnumerable
Arrays implement the IEnumerable
interface
for us automatically. This allows us to use arrays with LINQ extension methods, or pass arrays to methods that receive IEnumerable
.
IEnumerable
is an important part of using the C# language—it makes developing programs much easier.using System; using System.Collections.Generic; class Program { static void Display(IEnumerable<int> values) { // This method can be used with arrays or Lists. foreach (int value in values) { Console.WriteLine("IENUMERABLE: " + value); } } static void Main() { int[] ids = { 10400, 20800, 40100 }; // Pass the int array to the Display method, which accepts it as an IEnumerable. Display(ids); } }IENUMERABLE: 10400 IENUMERABLE: 20800 IENUMERABLE: 40100
Count
extensionIt is usually best to use the Length
property for an element count. But the Count()
extension method can be also used—here we use a lambda to only count certain elements.
using System; using System.Linq; var array = new int[] { 10, 20, 30 }; // Count elements greater than or equal to 20. int c = array.Count(x => x >= 20); Console.WriteLine(c);2
Suppose two arrays exist—we can combine them. The LINQ namespace provides us the Concat
extension method, which we can use with 2 arrays.
using System; using System.Linq; var values1 = new int[] { 1, 2 }; var values2 = new int[] { 3, 4 }; // Use Concat and ToArray to merge the values into a single array. var result = values1.Concat(values2).ToArray(); foreach (var value in result) { Console.WriteLine(value); }1 2 3 4
We can place an array inside another array, giving us a jagged array. Two-dimensional arrays can also be used (they use a comma in the index syntax).
using System; // Use jagged array. var values = new int[][] { new int[] { 1, 2 }, new int[] { 3, 4 }}; // The top left item. Console.WriteLine(values[0][0]); // The bottom right item. Console.WriteLine(values[1][1]);1 4
Suppose we want to test an array for a known list of constant elements. We can do this with the is
-operator in modern versions of the C# language.
using System; int[] values = [100, 400, 600]; // Test array with pattern matching syntax. if (values is [100, 200]) { Console.WriteLine("???"); } else if (values is [100, 400, 600]) { Console.WriteLine("Matched 100, 400, 600"); }Matched 100, 400, 600
We must only access elements in an array that are present in the array. The values can be anything, but the accesses must occur within the length of the array.
using System; class Program { static void Main() { int[] test = { 10, 20, 30 }; Console.WriteLine(test[4]); } }Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array. at Program.Main() in ...Program.cs:line 7
Creating an array has some cost—memory needs to be allocated, and it will need to be garbage-collected. We can optimize by caching arrays of the required sizes.
static
array of 0 elements each time, so less burden is placed on the runtime.using System; using System.Diagnostics; const int _max = 1000000; int[] emptyArrayCache = new int[0]; // Version 1: use an empty array cache to avoid creating more than 1 array. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { var temp = emptyArrayCache; if (temp.Length != 0) { return; } } s1.Stop(); // Version 2: use a new array on each method call. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { var temp = new int[0]; if (temp.Length != 0) { 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"));0.81 ns Cached array 6.36 ns New array
Arrays in C# are memory regions that contain elements. They store string
references, ints, bytes (or any type). Even if not used directly, they are a core part of all programs.