← Open in the full interactive course (progress tracking, search & more)

An array is a fixed number of same-typed values, sitting side by side in one block of memory, each reachable instantly by its index.

Imagine a row of exactly 7 mailboxes outside an apartment building, numbered 0 through 6. Each mailbox holds one letter. You don't need to search the row to find mailbox #3 — you walk straight to it, because you know exactly where it is. That's the entire idea behind an array.

So far in this course, you've stored one value per variable: int score = 95;. But real programs rarely deal with just one of anything — a game has multiple player scores, a store has multiple product prices, a calendar has multiple days. You need a way to hold many values of the same type, keep them in order, and get to any one of them instantly.

In this lesson, you'll learn what an array is, why it exists, how it's laid out in memory, and how to use it correctly — including the mistakes almost every beginner makes at least once.

What Is It?

The Simple Explanation

An array is a container that holds a fixed number of values, all of the same type, in a specific order. Each value has a numbered position called an index, starting at 0. If you have 5 values, their indexes are 0, 1, 2, 3, 4 — never 5.

The Technical Definition

In C#, an array is a reference type that represents a fixed-size, zero-indexed, ordered collection of elements of a single type. Once created, its length (the number of elements) cannot change. The type is written as T[], where T is the element type — for example, int[], string[], or Product[].

Array = Type + Fixed Length + Index

Modern C# lets you create an array with a collection expression — clean square-bracket syntax:

int[] scores = [95, 82, 71, 100, 60]; // a fixed array of 5 integers string[] days = ["Mon", "Tue", "Wed"]; // a fixed array of 3 strings

Why Does It Exist?

The Problem

Suppose you need to record the test scores of 5 students. Without arrays, you'd have to declare 5 separate variables:

int score1 = 95; int score2 = 82; int score3 = 71; int score4 = 100; int score5 = 60;

This falls apart quickly:

The Need

You need one variable that represents many values of the same type, that you can loop over, index into, and pass around as a single unit.

The Solution

An array solves exactly this. One variable, scores, refers to all 5 values at once:

int[] scores = [95, 82, 71, 100, 60]; foreach (int s in scores) Console.WriteLine(s); // prints all 5, no repetition

Arrays are also the foundation everything else in this module builds on — List<T>, which you'll meet in the next lesson, literally wraps an array internally to give you a resizable version of the same idea.

Big Picture

AN ARRAY OF 5 INTEGERS — IN MEMORY
95
[0]
82
[1]
71
[2]
100
[3]
60
[4]
5 boxes, side by side, no gaps. scores[3] jumps straight to the 4th box — no searching required.

How It Works

USING AN ARRAY, STEP BY STEP
1. DECLARE AND CREATE
int[] scores = new int[5];   // 5 slots, all default to 0
int[] scores2 = [95, 82, 71, 100, 60]; // 5 slots, filled immediately
2. READ AND WRITE BY INDEX
int first = scores[0];   // read → 95
scores[2] = 99;          // write → replaces 71 with 99
3. CHECK THE LENGTH
Console.WriteLine(scores.Length);   // 5 — always, never changes
4. ITERATE
foreach (int s in scores)
    Console.WriteLine(s);

for (int i = 0; i < scores.Length; i++)
    Console.WriteLine($"[{i}] = {scores[i]}");

Simple Example

int[] scores = [95, 82, 71, 100, 60]; int total = 0; foreach (int s in scores) total += s; double average = (double)total / scores.Length; Console.WriteLine($"Scores: {string.Join(", ", scores)}"); Console.WriteLine($"Total: {total}"); Console.WriteLine($"Average: {average:F1}"); // Scores: 95, 82, 71, 100, 60 // Total: 408 // Average: 81.6

Code → Meaning → Result:

Real-World Example

A small warehouse tracks stock counts across exactly 5 fixed aisles. The number of aisles is a physical constant — it never changes at runtime — which makes it a textbook use case for an array rather than a resizable collection.

string[] aisleNames = ["Electronics", "Groceries", "Clothing", "Toys", "Books"]; int[] stockCounts = [120, 340, 85, 60, 210]; Console.WriteLine("Warehouse Stock Report"); Console.WriteLine("----------------------"); for (int i = 0; i < aisleNames.Length; i++) { Console.WriteLine($"Aisle {i + 1} ({aisleNames[i]}): {stockCounts[i]} units"); } int lowestIndex = 0; for (int i = 1; i < stockCounts.Length; i++) { if (stockCounts[i] < stockCounts[lowestIndex]) lowestIndex = i; } Console.WriteLine($"\nLowest stock: {aisleNames[lowestIndex]} ({stockCounts[lowestIndex]} units)"); // Warehouse Stock Report // ---------------------- // Aisle 1 (Electronics): 120 units // Aisle 2 (Groceries): 340 units // Aisle 3 (Clothing): 85 units // Aisle 4 (Toys): 60 units // Aisle 5 (Books): 210 units // // Lowest stock: Toys (60 units)

Notice that aisleNames and stockCounts are two parallel arrays — index 2 in one corresponds to index 2 in the other. This pattern is common with plain arrays, though as you'll see in later lessons, a single array of small objects (or a Dictionary) is often a cleaner design once the data grows more complex.

Analogy

An Egg Carton

An array is like a standard egg carton with exactly 12 molded slots. You can't add a 13th egg — there's no slot for it. You can't remove a slot either. What you can do is take an egg out of slot 4, or put a different egg into slot 4. The carton's size — 12 — is fixed the moment it was manufactured.

That's precisely how a C# array behaves: the number of slots (Length) is fixed at creation. You can freely change what's in each slot, but you can never add or remove slots from the array itself.

Under the Hood

HOW THE RUNTIME LAYS OUT AN ARRAY
1. ONE CONTIGUOUS BLOCK ON THE HEAP
2. INDEXING IS JUST ARITHMETIC
3. BOUNDS CHECKING
4. VALUE TYPES vs REFERENCE TYPES INSIDE AN ARRAY

Common Confusion

1. "Array" vs "List" — aren't they the same thing?

Not quite — you'll meet List<T> in the next lesson, but the short version: an array's size is locked forever once created; a List<T> can grow and shrink with .Add() and .Remove(). Under the hood, a List<T> is actually built on top of an array — so understanding arrays first is what makes List<T> make sense.

2. An array is a reference type, even for value-type elements

An int[] array is itself a reference type (a class-like object on the heap), even though it stores int values. This matters when you pass an array to a method: the method receives a reference to the same array, so changes it makes to the elements are visible to the caller too — unlike passing a plain int, which is copied.

void DoubleAll(int[] numbers) { for (int i = 0; i < numbers.Length; i++) numbers[i] *= 2; } int[] values = [1, 2, 3]; DoubleAll(values); Console.WriteLine(string.Join(", ", values)); // 2, 4, 6 — the original changed!

3. Reassigning an array element vs resizing the array

scores[2] = 99; is allowed — you're replacing what's in a slot. There is no scores.Add(50) — arrays have no such method, because that would require changing the length, which arrays cannot do.

Common Mistakes

Mistake 1 — Off-by-one indexing

Wrong — this throws IndexOutOfRangeException:

int[] scores = [95, 82, 71, 100, 60]; Console.WriteLine(scores[5]); // valid indexes are 0–4, not 5

Correct — the last valid index is always Length - 1:

Console.WriteLine(scores[scores.Length - 1]); // 60

Mistake 2 — Trying to "add" to an array

Wrong — arrays have no Add method:

int[] scores = [95, 82, 71]; scores.Add(100); // compiler error — Add doesn't exist on arrays

Correct — if the size genuinely needs to grow, use List<T> instead (covered next lesson), or create a new, larger array and copy the old data into it.

Mistake 3 — Forgetting that arrays are shared references

Assigning one array variable to another does not make a copy — both names point at the same block of memory:

int[] original = [1, 2, 3]; int[] alias = original; // not a copy — same array! alias[0] = 99; Console.WriteLine(original[0]); // 99 — original changed too

Correct — to make an independent copy, use Clone() or LINQ's ToArray() (LINQ is covered in a later part of this course):

int[] copy = (int[])original.Clone();

When Should I Use It?

Use an array when

Reach for something else when

Rule of thumb: If you find yourself wanting to add or remove elements after the array is created, that's your signal you actually want List<T> — the subject of the next lesson.

Mental Model

Array = a fixed row of same-typed boxes, numbered from 0
Index = the box number — use it to jump straight to a value
Length = how many boxes exist, forever

Remember:
· Indexes run from 0 to Length - 1 — never Length itself.
· Size is fixed at creation — no Add, no Remove.
· Reading/writing any index is instant (O(1)) because elements sit contiguously in memory.
· Arrays are reference types — passing one to a method shares the same data, it doesn't copy it.

Key Takeaway


Check Your Understanding

You've seen how arrays store fixed-size, indexed data in contiguous memory. Let's check that it's stuck.

1. Given int[] values = [10, 20, 30];, what is values[3]?

Show answer

Correct: C

Why C is correct: The array has 3 elements at indexes 0, 1, and 2. Index 3 doesn't exist — the runtime checks bounds on every access and throws IndexOutOfRangeException rather than reading unrelated memory.

Why A is incorrect: Default values only apply to slots that were never explicitly set — there is no slot 3 at all here.

Why B is incorrect: 30 is at index 2 (since indexing starts at 0), not index 3.

Why D is incorrect: null would only be a plausible default for a reference-type array, and even then, index 3 still doesn't exist.

Reinforcement: For a 3-element array, valid indexes are 0, 1, 2 — never the element count itself.

2. Why is reading scores[1000] from a 1000-element array just as fast as reading scores[0]?

Show answer

Correct: B

Why B is correct: Because array elements are laid out back-to-back with no gaps, the runtime finds any element's address with base_address + (index × element_size) — one calculation, regardless of how far into the array the index is. This constant-time behavior is called O(1).

Why A is incorrect: Arrays don't use a caching layer for element access — the direct address calculation is already as fast as it gets.

Why C is incorrect: Arrays aren't automatically sorted, and index access doesn't use search algorithms at all — it's direct addressing.

Why D is incorrect: Index access time does not depend on how large the index is; that's exactly the point of contiguous, indexed memory.

Reinforcement: Contiguous memory + a numeric index = instant access, no matter the array's size.

3. A method receives an int[] parameter and modifies one of its elements. What happens to the caller's original array?

Show answer

Correct: B

Why B is correct: Arrays are reference types. Passing an array to a method passes a reference to the same underlying memory block — there is only ever one array here, so changes made inside the method are visible to the caller.

Why A is incorrect: That's true for value types like int, but not for arrays — arrays are reference types and are not deep-copied automatically.

Why C is incorrect: Arrays are passed as parameters constantly — it's one of their most common uses.

Why D is incorrect: The method receives the actual array, fully populated — not an empty one.

Reinforcement: Because arrays are reference types, "passing an array" means sharing it, not cloning it.

4. Which scenario is the best fit for a plain array rather than a resizable collection?

Show answer

Correct: B

Why B is correct: There are always exactly 12 months. The size is known in advance and will never change, which is the defining case for a fixed-size array.

Why A is incorrect: A shopping cart's item count changes constantly as the customer shops — that calls for a resizable List<T>, covered next.

Why C is incorrect: An ever-growing subscriber list needs a collection that can expand — not a fixed array.

Why D is incorrect: A queue of jobs being added and removed needs a growable, order-aware collection — you'll meet Queue<T> for exactly this later in this module.

Reinforcement: Reach for an array only when the element count is genuinely fixed and known ahead of time.

5. What does int[] copy = original; actually do?

Show answer

Correct: B

Why B is correct: Since arrays are reference types, a plain assignment copies the reference, not the data. copy and original now both point at the same block of memory — changing one through either name affects both.

Why A is incorrect: No new array is allocated by a plain assignment — that requires an explicit copy operation such as Clone().

Why C is incorrect: This is perfectly valid C# — it just doesn't do what a beginner might expect.

Why D is incorrect: Assignment doesn't copy individual elements at all — it copies the reference to the whole array.

Reinforcement: To get an independent copy of an array's data, you must copy it explicitly (e.g. with Clone()) — plain assignment shares the same memory.

You now understand arrays — the foundation every other collection in .NET builds on. Next up: List<T>, the resizable cousin of the array.


dotnetmadeeasy.com — Learn C# and .NET, the right way.