C# Array Declaration: Syntax and Usage
c# array declaration: Understand C# array declaration syntax, initialization patterns, and the tradeoffs between arrays and List for performance.
When you declare an array in C#, you are allocating a fixed-size block of memory on the heap. The syntax is straightforward, but the choices you make during declaration affect type safety, performance, and maintainability. This article covers the core C# array declaration patterns and the tradeoffs involved.
Basic Array Declaration Syntax
The most common way to declare an array is to specify the element type followed by square brackets, then assign a new array instance with a given length:
int[] numbers = new int[5];
This creates an array of five integers, each initialized to the default value for int, which is 0. The variable numbers holds a reference to the array object. The length is fixed at creation time and cannot be changed later.
You can also separate declaration from assignment:
int[] numbers; numbers = new int[5];
This is useful when the array is assigned conditionally or passed as an argument. The declared variable is null until you assign an array to it.
Initializing Arrays with Values
If you know the values at compile time, you can use an array initializer:
int[] primes = { 2, 3, 5, 7, 11 };
The compiler infers the length from the number of elements. You can also use the new keyword with the initializer:
int[] primes = new int[] { 2, 3, 5, 7, 11 };
Both forms are equivalent. The shorter form is idiomatic, but the explicit new int[] makes the type clear when the variable type is not immediately obvious, such as when using var.
Multidimensional and Jagged Arrays
C# supports two distinct kinds of multi-dimensional arrays: rectangular and jagged.
A rectangular array has a fixed number of rows and columns, and is declared with a comma inside the brackets:
int[,] matrix = new int[3, 4];
This creates a 3x4 matrix. All rows have the same length, and memory is allocated as a single contiguous block.
A jagged array is an array of arrays, where each inner array can have a different length:
int[][] jagged = new int[3][]; jagged[0] = new int[4]; jagged[1] = new int[2]; jagged[2] = new int[6];
Jagged arrays are more flexible but require separate allocation for each inner array. Access syntax also differs: matrix[1,2] for rectangular, jagged[1][2] for jagged.
Using var for Array Declaration
The var keyword lets you declare an array without writing the type explicitly:
var numbers = new[] { 10, 20, 30, 40 };
The compiler infers the element type from the initializer. This works well when the type is obvious, but it can reduce readability if the initializer is complex. If you need to declare an empty array, you must still specify the type, because var requires an initializer that provides the type.
Array vs List: When to Use Which
Arrays and List<T> both store sequences of elements, but they differ in resizability and allocation behavior. An array has a fixed size; a List<T> can grow dynamically. List<T> internally uses an array and resizes it when needed, which involves copying the existing elements.
| Feature | Array | List<T> |
|---|---|---|
| Size | Fixed at creation | Dynamic |
| Memory overhead | Minimal | Slight overhead |
| Resize cost | Not applicable | O(n) on resize |
| Index access | O(1) | O(1) |
| Type safety | Strong | Strong |
Use an array when the number of elements is known and fixed, and when you need the lowest possible memory overhead. Use a List<T> when the collection size changes at runtime, or when you need methods like Add, Remove, or Contains without implementing them manually.
Common Mistakes and Edge Cases
A frequent mistake is forgetting that arrays are zero-based. Accessing numbers[5] on an array of length 5 throws an IndexOutOfRangeException. Always check the Length property or use for loops with i < array.Length.
Another subtle issue is array covariance. In C#, an array of a reference type can be assigned to an array of its base type:
string[] strings = new string[10]; object[] objects = strings;
This is allowed, but it can lead to runtime errors if you try to assign a non-string object to objects[0]. The runtime throws ArrayTypeMismatchException because the underlying array is still a string[].
Empty arrays are also a common source of confusion. An empty array has length zero and is not null. You can create one with Array.Empty<T>() to avoid allocating a new empty array each time, which is a small but useful optimization in high-traffic code.
Performance and Memory Considerations
Array allocation happens on the managed heap. The size of the array is fixed at creation, and the runtime reserves a contiguous block of memory. This makes array access very fast, but it also means you cannot change the size without creating a new array and copying the data.
For large arrays, consider the memory footprint. A int[] of length 1,000,000 consumes about 4 MB on a 32-bit integer, plus a small object header. If you need to store value types, arrays store them inline, which is more efficient than a List<T> of boxed objects, because List<T> is generic and also stores values inline for value types. However, compared to a List<T>, an array has no extra capacity slack. A List<T> often reserves more memory than its Count to reduce future resizes, so an array can be more memory-efficient when the size is fixed.
When performance is critical, and you know the exact size at compile time or design time, an array avoids the overhead of dynamic resizing and the extra capacity of a List<T>. For example, a fixed lookup table or a buffer used in a tight loop is often better as an array.