07. Arrays in C#
Arrays store fixed-size collections of same-type elements. Learn single-dimensional, multi-dimensional, and jagged arrays. Understand indexing, iteration, and when to use arrays vs List<T>. Real School Management System examples throughout.
An array stores a fixed number of elements of the same type in a sequence. Each element has an index starting from 0.
Use arrays when you know the exact number of elements upfront - for example, 5 subjects, 12 months, or 3 sections in a school.
Key concepts:
- Fixed size (set at creation, cannot grow)
- Zero-indexed (first element is index 0)
- Same data type for all elements
- Fixed in memory, slightly faster than List<T>
Array Types Reference
| Type | Syntax | Use Case | Example |
|---|---|---|---|
| Single-Dimensional | int[] | Simple list of values | Student marks (82, 91, 78) |
| Multi-Dimensional | int[,] | Table/grid of data | Class marks (3 students - 5 subjects) |
| Jagged | int[][] | Rows of different lengths | Classes with varying subject counts |
Declaring and Initialising Arrays
// Declare an array of 5 integers
int[] marks = new int[5];
// Declare and initialise at the same time
int[] marks = { 82, 91, 78, 85, 88 };
// Another way to initialise
int[] marks = new int[] { 82, 91, 78, 85, 88 };
// String array - subject names
string[] subjects = { "English", "Maths", "Science", "Social", "Language" };
Accessing Array Elements
Indexing - 0-based:
- First element: index 0
- Last element: index Length - 1
- Out of bounds throws
IndexOutOfRangeException
int[] marks = { 82, 91, 78, 85, 88 };
// Index starts at 0
int englishMarks = marks[0]; // 82 (first)
int mathsMarks = marks[1]; // 91
int lastSubject = marks[4]; // 88 (last = Length - 1)
// Length of array
int count = marks.Length; // 5
// Modify an element
marks[0] = 90;
// Wrong - IndexOutOfRangeException!
int invalid = marks[5]; // Index 5 doesn't exist (only 0-4)
// Correct - bounds check
if (index >= 0 && index < marks.Length)
{
int value = marks[index];
}
Array size is fixed:
int[] marks = new int[5]; // Size = 5, cannot change
marks[10] = 100; // Error - out of bounds
marks = new int[10]; // ? OK - create new array
Iterating Over Arrays
for loop - when you need the index:
int[] marks = { 82, 91, 78, 85, 88 };
for (int i = 0; i < marks.Length; i++)
{
Console.WriteLine("Subject " + (i + 1) + " : " + marks[i]);
}
// Output:
// Subject 1 : 82
// Subject 2 : 91
// ...
foreach loop - when you just need each value:
int[] marks = { 82, 91, 78, 85, 88 };
foreach (int mark in marks)
{
Console.WriteLine("Marks: " + mark);
}
// Output:
// Marks: 82
// Marks: 91
// ...
When to use each:
| Loop Type | Use When | Can Access Index? |
|---|---|---|
for | Need index, need to modify elements, skip elements | ? Yes |
foreach | Just reading values, simple iteration | ? No |
for vs foreach:
// for - modify values
for (int i = 0; i < marks.Length; i++)
{
marks[i] = marks[i] + 5; // Add 5 to each mark
}
// foreach - cannot modify (read-only)
foreach (int mark in marks)
{
mark = mark + 5; // ? Does NOT modify original array!
}
School Management - Student Marks Array
Array Properties and Methods
int[] marks = { 82, 91, 78, 85, 88 };
// Length - number of elements
Console.WriteLine(marks.Length); // 5
// Sort - ascending order
Array.Sort(marks);
// marks is now: { 78, 82, 85, 88, 91 }
// Reverse - after sort, gives descending
Array.Reverse(marks);
// marks is now: { 91, 88, 85, 82, 78 }
// Find minimum and maximum
int highest = marks[0]; // after sort descending
int lowest = marks[marks.Length - 1];
// Copy array
int[] backup = new int[marks.Length];
Array.Copy(marks, backup, marks.Length);
// Search
int position = Array.IndexOf(marks, 85); // returns index of value 85
Multi-Dimensional Arrays
A 2D array stores data in rows and columns - like a table.
// 3 students - 5 subjects - store all marks in one table
int[,] classMarks = new int[3, 5]
{
{ 82, 91, 78, 85, 88 }, // Sahasra Kumar
{ 74, 85, 90, 78, 82 }, // Priya Sharma
{ 88, 76, 84, 92, 79 } // Arjun Reddy
};
// Access: row 0, column 1 (Sahasra's Maths marks)
int raviMaths = classMarks[0, 1]; // 91
// Iterate all rows and columns
string[] studentNames = { "Sahasra Kumar", "Priya Sharma", "Arjun Reddy" };
string[] subjectNames = { "English", "Maths", "Science", "Social", "Language" };
for (int student = 0; student < classMarks.GetLength(0); student++)
{
Console.WriteLine("=== " + studentNames[student] + " ===");
for (int subject = 0; subject < classMarks.GetLength(1); subject++)
{
Console.WriteLine(subjectNames[subject] + " : " + classMarks[student, subject]);
}
}
Jagged Arrays (Array of Arrays)
A jagged array is an array where each row can have a different number of columns.
// Different number of subjects per class
// Class 10 has 5 subjects, Class 11 has 6 subjects, Class 12 has 7 subjects
int[][] subjectsPerClass = new int[3][];
subjectsPerClass[0] = new int[] { 82, 91, 78, 85, 88 }; // 5 subjects
subjectsPerClass[1] = new int[] { 74, 85, 90, 78, 82, 79 }; // 6 subjects
subjectsPerClass[2] = new int[] { 88, 76, 84, 92, 79, 83, 87 }; // 7 subjects
// Access
int firstClassFirstSubject = subjectsPerClass[0][0]; // 82
// Iterate
for (int i = 0; i < subjectsPerClass.Length; i++)
{
Console.Write("Class " + (i + 10) + "th marks: ");
for (int j = 0; j < subjectsPerClass[i].Length; j++)
{
Console.Write(subjectsPerClass[i][j] + " ");
}
Console.WriteLine();
}
Array vs List<T>
| Array | List<T> | |
|---|---|---|
| Size | Fixed - set at creation | Dynamic - grows automatically |
| Performance | Slightly faster | Slightly slower |
| Add/Remove | Cannot add or remove | Can add, remove, insert |
| Use when | Size is known and fixed | Size changes at runtime |
// Array - fixed 5 subjects
int[] marks = new int[5];
// List - students can be added any time
List<string> studentNames = new List<string>();
studentNames.Add("Sahasra Kumar");
studentNames.Add("Priya Sharma");
Accessing marks[5] when array has 5 elements (indices 0-4) throws IndexOutOfRangeException. Always check index < array.Length before accessing.
Common Mistakes
Off-by-one error:
int[] marks = { 82, 91, 78, 85, 88 }; // Length = 5, indices 0-4
// Wrong - tries to access index 5
for (int i = 0; i <= marks.Length; i++) // Should be i < marks.Length
{
Console.WriteLine(marks[i]); // Crashes on last iteration!
}
// Correct
for (int i = 0; i < marks.Length; i++)
{
Console.WriteLine(marks[i]);
}
Assuming array can grow:
int[] marks = new int[5];
// ... later in code ...
marks[10] = 95; // Error - array size is fixed!
// ? Use List<T> if size changes
List<int> marksList = new List<int>();
marksList.Add(82);
marksList.Add(91); // Can add as many as needed
foreach modifies copy, not original:
int[] marks = { 82, 91, 78 };
// Wrong - mark is a copy, doesn't modify array
foreach (int mark in marks)
{
mark = mark + 10;
}
Console.WriteLine(marks[0]); // Still 82, not 92!
// Correct - use for loop to modify
for (int i = 0; i < marks.Length; i++)
{
marks[i] = marks[i] + 10;
}
Console.WriteLine(marks[0]); // Now 92
Null reference with uninitialized elements:
string[] names = new string[3]; // All are null initially
// Wrong - NullReferenceException
Console.WriteLine(names[0].Length);
// Correct - check for null
if (names[0] != null)
{
Console.WriteLine(names[0].Length);
}
// ? Or initialize properly
string[] names = { "Sahasra", "Priya", "Arjun" };
Best Practices
-
Always check bounds before accessing:
if (index >= 0 && index < array.Length){value = array[index];} -
Use for loop to modify, foreach to read:
// Readforeach (int mark in marks)sum += mark;// Modifyfor (int i = 0; i < marks.Length; i++)marks[i] = marks[i] * 2; -
Use List<T> when size changes:
// Array - fixed sizeint[] studentIds = new int[100];// List - dynamicList<int> studentIds = new List<int>();studentIds.Add(1001);studentIds.Add(1002); -
Use meaningful array sizes:
// ? Magic numberint[] marks = new int[5];// ? Named constantconst int SUBJECT_COUNT = 5;int[] marks = new int[SUBJECT_COUNT]; -
Initialize multi-dimensional arrays carefully:
// ? Clear what the dimensions meanint[,] classMarks = new int[studentCount, subjectCount];// Not: new int[3, 5]
Array:
- Fixed size (set at creation, cannot change)
- Slightly faster (contiguous memory)
- Use when size is known and constant
List<T>:
- Dynamic size (grows automatically)
- Can add, remove, insert elements
- Slightly slower than array
- Use in most real code
When to use:
// Array - class has exactly 5 subjects
int[] marks = new int[5];
// List - student count varies
List<int> studentIds = new List<int>();
studentIds.Add(1001);
studentIds.Add(1002);
Internally: List<T> uses array + resizing logic. When full, it creates larger array and copies data.
IndexOutOfRangeException is thrown at runtime.
int[] marks = { 82, 91, 78, 85, 88 }; // Length = 5, indices 0-4
Console.WriteLine(marks[5]); // ? IndexOutOfRangeException!
Why? Array has indices 0, 1, 2, 3, 4. Index 5 doesn't exist.
Fix:
// Check before accessing
if (index >= 0 && index < marks.Length)
{
int value = marks[index];
}
// Or use try-catch (not recommended)
try
{
int value = marks[5];
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Invalid index");
}
Best practice: Always validate index bounds before access.
No. Array is strongly typed.
int[] marks = { 82, 91, 78 }; // ? Only integers
string[] names = { "Sahasra", "Priya" }; // ? Only strings
// Wrong - type mismatch
int[] mixed = { 82, "Sahasra", true }; // Compiler error!
If you need multiple types:
Option 1: Use object[] (with boxing penalty)
object[] data = { 82, "Sahasra", true }; // ? Works (loses type safety)
int marks = (int)data[0]; // Need to cast
Option 2: Use a class to group related data
class StudentRecord
{
public int marks;
public string name;
public bool isActive;
}
StudentRecord[] records = new StudentRecord[10];
Option 3: Use Tuple or record
var data = (marks: 82, name: "Sahasra", active: true);
Best practice: Use a class or record, not object[].
for loop:
- Access index: ? Yes
- Modify elements: ? Yes
- Skip/reverse: ? Yes
- When to use: Need control over iteration
foreach loop:
- Access index: ? No
- Modify elements: ? No (modifies copy only)
- Skip/reverse: ? No
- When to use: Simple read-only iteration
Example:
int[] marks = { 82, 91, 78, 85, 88 };
// for - can modify
for (int i = 0; i < marks.Length; i++)
{
marks[i] = marks[i] + 10; // ? Updates original
}
// foreach - read only
foreach (int mark in marks)
{
mark = mark + 10; // ? Updates copy only, not original
}
Performance: Both similar; foreach slightly cleaner for read-only.
2D arrays store data in rows and columns - like a table/spreadsheet.
// 3 students - 5 subjects
int[,] classMarks = new int[3, 5]
{
{ 82, 91, 78, 85, 88 }, // Sahasra
{ 74, 85, 90, 78, 82 }, // Priya
{ 88, 76, 84, 92, 79 } // Arjun
};
// Access: [row, column]
int raviMaths = classMarks[0, 1]; // 91
When to use:
- Spreadsheet-like data (rows - columns)
- ? Game board (chess, tic-tac-toe)
- ? Matrix calculations
- Class marks (students - subjects)
Multi-dimensional methods:
int rows = classMarks.GetLength(0); // 3 (students)
int cols = classMarks.GetLength(1); // 5 (subjects)
// Iterate all elements
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Console.Write(classMarks[i, j] + " ");
}
Console.WriteLine();
}
3D arrays (cube):
int[,,] schoolData = new int[10, 12, 50]; // 10 classes - 12 months - 50 students
Rule: Multi-dimensional = nested loops.
Multi-dimensional array: All rows same length (rectangular). Jagged array: Each row can have different length (irregular).
// Jagged array - each row different size
int[][] classSubjects = new int[3][];
classSubjects[0] = new int[] { 82, 91, 78, 85, 88 }; // 5 subjects
classSubjects[1] = new int[] { 74, 85, 90, 78, 82, 79 }; // 6 subjects
classSubjects[2] = new int[] { 88, 76, 84, 92, 79, 83, 87 }; // 7 subjects
// Access
int firstClassFirstSubject = classSubjects[0][0]; // 82
int secondClassFifthSubject = classSubjects[1][4]; // 82
When to use jagged:
- Different row sizes (different grades have different subject counts)
- Sparse data (some rows have few elements)
- Tree/graph structures
Jagged vs Multi-dimensional:
// Multi-dimensional - rectangular (3-5)
int[,] rect = new int[3, 5];
// Jagged - irregular
int[][] jagged = new int[3][];
jagged[0] = new int[5];
jagged[1] = new int[6];
jagged[2] = new int[7];
Memory: Jagged uses more pointers but saves memory if rows vary.
Use ChatGPT, Claude, or Copilot to go deeper on C# arrays. Try these prompts:
"What is the difference between array, List, and ArrayList in C#?""When should I use arrays vs List<T> in school management?""How do I find the maximum marks in an int array without using LINQ?""Quiz me on arrays - declare, iterate, bounds, multi-dimensional"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.