Easy Learn C#

C# Arrays

Introduction to Arrays

An array is a collection of elements of the same type, stored in contiguous memory locations. Arrays provide a way to store multiple values of the same data type under a single variable name, with each value accessible by its position (index) in the array.

Key Array Concepts:

  • Arrays store multiple values of the same type
  • Array elements are accessed by their index (zero-based)
  • Array size is fixed at creation time
  • Arrays can be single-dimensional, multi-dimensional, or jagged
  • Arrays are reference types, even when they store value types
  • All arrays in C# inherit from the System.Array class

Declaring and Initializing Arrays

C# provides several ways to declare and initialize arrays, depending on your specific needs.

Array Declaration and Initialization:


// Declaration without initialization
int[] numbers;            // Preferred syntax
string[] names;           // Array of strings
double[] prices;          // Array of doubles

// Declaration with array size
int[] scores = new int[5];  // Creates an array of 5 integers, all initialized to 0

// Declaration with initialization
int[] ages = new int[] { 25, 30, 42, 18, 50 };  // Specify values directly
int[] years = { 2020, 2021, 2022, 2023 };       // Shorthand syntax

// Declaration, creation, and initialization in separate steps
string[] fruits;                     // Declaration
fruits = new string[3];              // Creation with size
fruits[0] = "Apple";                 // Initialization by index
fruits[1] = "Banana";
fruits[2] = "Cherry";

// Alternative initialization using object initialization syntax
int[] fibonacci = new int[6] { 1, 1, 2, 3, 5, 8 };
                            

When you initialize an array without explicit values:

  • Numeric types are initialized to 0
  • Boolean types are initialized to false
  • Reference types (including strings) are initialized to null

Accessing Array Elements

Array elements are accessed using zero-based indices. The first element is at index 0, the second at index 1, and so on.

Accessing and Modifying Array Elements:


// Create an array
string[] colors = { "Red", "Green", "Blue", "Yellow", "Purple" };

// Accessing elements by index
string firstColor = colors[0];   // "Red"
string thirdColor = colors[2];   // "Blue"
Console.WriteLine($"The first color is {firstColor}");
Console.WriteLine($"The third color is {thirdColor}");

// Modifying elements
colors[1] = "Emerald";           // Change "Green" to "Emerald"
colors[4] = "Violet";            // Change "Purple" to "Violet"

// Accessing array elements in a loop
for (int i = 0; i < colors.Length; i++)
{
    Console.WriteLine($"Color at index {i}: {colors[i]}");
}

// Getting the array length
int numberOfColors = colors.Length;  // 5
Console.WriteLine($"The array contains {numberOfColors} colors");

// Attempting to access an index outside the array bounds
// colors[-1] = "Invalid";  // Runtime error: IndexOutOfRangeException
// colors[5] = "Invalid";   // Runtime error: IndexOutOfRangeException

// Safe access using bounds checking
int index = 5;
if (index >= 0 && index < colors.Length)
{
    Console.WriteLine($"Color at index {index}: {colors[index]}");
}
else
{
    Console.WriteLine($"Index {index} is out of bounds");
}
                            

Important notes about array access:

  • Always use the Length property to determine the size of an array
  • Array access is very fast (constant time operation)
  • Accessing an index outside the array bounds causes an IndexOutOfRangeException
  • Always validate indices when they come from user input or external sources

Iterating Through Arrays

C# offers several ways to iterate through array elements, with the for loop and foreach loop being the most common.

Iterating Through Arrays:


// Sample array
int[] numbers = { 10, 20, 30, 40, 50 };

// Using a for loop (when you need the index)
Console.WriteLine("Using for loop:");
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine($"Element at index {i}: {numbers[i]}");
}

// Using a foreach loop (simpler syntax when you don't need the index)
Console.WriteLine("\nUsing foreach loop:");
foreach (int number in numbers)
{
    Console.WriteLine($"Value: {number}");
}

// Accessing in reverse order
Console.WriteLine("\nReverse order using for loop:");
for (int i = numbers.Length - 1; i >= 0; i--)
{
    Console.WriteLine($"Element at index {i}: {numbers[i]}");
}

// Using Array.ForEach method
Console.WriteLine("\nUsing Array.ForEach method:");
Array.ForEach(numbers, number => Console.WriteLine($"Value: {number}"));

// Using LINQ methods (requires using System.Linq)
Console.WriteLine("\nUsing LINQ:");
numbers.ToList().ForEach(number => Console.WriteLine($"Value: {number}"));
                            

Choosing the right iteration method:

  • Use for when you need the index or need to modify array elements
  • Use foreach for cleaner code when you just need to read values
  • Use Array.ForEach or LINQ for more functional programming style

Common Array Processing Patterns:


// Finding the sum of array elements
int[] values = { 5, 8, 12, 15, 3 };
int sum = 0;

foreach (int value in values)
{
    sum += value;
}
Console.WriteLine($"Sum: {sum}");  // 43

// Finding the average
double average = (double)sum / values.Length;
Console.WriteLine($"Average: {average}");  // 8.6

// Finding the maximum and minimum values
int max = values[0];
int min = values[0];

for (int i = 1; i < values.Length; i++)
{
    if (values[i] > max)
    {
        max = values[i];
    }
    if (values[i] < min)
    {
        min = values[i];
    }
}

Console.WriteLine($"Maximum: {max}");  // 15
Console.WriteLine($"Minimum: {min}");  // 3

// Counting elements that meet a condition
int[] scores = { 75, 92, 83, 65, 98, 70 };
int passingCount = 0;

foreach (int score in scores)
{
    if (score >= 70)
    {
        passingCount++;
    }
}

Console.WriteLine($"{passingCount} out of {scores.Length} students passed");  // 5 out of 6

// Filtering values
List evenNumbers = new List();

foreach (int number in values)
{
    if (number % 2 == 0)
    {
        evenNumbers.Add(number);
    }
}

Console.WriteLine($"Even numbers: {string.Join(", ", evenNumbers)}");  // 8, 12
                            

Common Array Methods

The System.Array class provides many useful methods for working with arrays.

Array Methods and Properties:


// Sample array
int[] numbers = { 5, 2, 9, 1, 7, 3 };

// Sort the array (in-place)
Array.Sort(numbers);
Console.WriteLine("Sorted array: " + string.Join(", ", numbers));  // 1, 2, 3, 5, 7, 9

// Reverse the array (in-place)
Array.Reverse(numbers);
Console.WriteLine("Reversed array: " + string.Join(", ", numbers));  // 9, 7, 5, 3, 2, 1

// Binary search (array must be sorted first)
Array.Sort(numbers);  // Sort again after reversing
int index = Array.BinarySearch(numbers, 5);
Console.WriteLine($"Found 5 at index: {index}");  // Will return the index of 5

// Check if an element exists
bool contains7 = Array.Exists(numbers, element => element == 7);
Console.WriteLine($"Array contains 7: {contains7}");  // True

// Find the first element that satisfies a condition
int firstAbove5 = Array.Find(numbers, element => element > 5);
Console.WriteLine($"First element above 5: {firstAbove5}");  // 7

// Find all elements that satisfy a condition
int[] allAbove3 = Array.FindAll(numbers, element => element > 3);
Console.WriteLine("All elements above 3: " + string.Join(", ", allAbove3));  // 5, 7, 9

// Convert all elements with Array.ConvertAll
string[] numberStrings = Array.ConvertAll(numbers, element => element.ToString());
Console.WriteLine("Converted to strings: " + string.Join(", ", numberStrings));

// Fill a range with a value
int[] filledArray = new int[10];
Array.Fill(filledArray, 42, 3, 5);  // Fill 5 elements starting at index 3 with value 42
Console.WriteLine("Filled array: " + string.Join(", ", filledArray));

// Clear a range of elements (sets to default value)
Array.Clear(numbers, 2, 2);  // Clear 2 elements starting at index 2
Console.WriteLine("After clearing: " + string.Join(", ", numbers));

// Copy array elements
int[] sourceArray = { 10, 20, 30, 40, 50 };
int[] destArray = new int[sourceArray.Length];
Array.Copy(sourceArray, destArray, sourceArray.Length);
Console.WriteLine("Copied array: " + string.Join(", ", destArray));

// Alternative way to copy an array
int[] clonedArray = (int[])sourceArray.Clone();
Console.WriteLine("Cloned array: " + string.Join(", ", clonedArray));

// Resize an array (creates a new array)
Array.Resize(ref sourceArray, 7);  // Resize to 7 elements (adds default values)
Console.WriteLine("Resized array: " + string.Join(", ", sourceArray));
                            

Important notes about array methods:

  • Most array modification methods operate on the array in-place
  • Array.Resize creates a new array and copies elements, rather than truly resizing
  • Binary search only works correctly on sorted arrays
  • Many array methods have equivalent LINQ methods that can be more expressive

Multi-Dimensional Arrays

C# supports two types of multi-dimensional arrays: rectangular arrays (with fixed rows and columns) and jagged arrays (arrays of arrays).

Rectangular Arrays (2D):


// Declaring a 2D array (3 rows, 4 columns)
int[,] matrix = new int[3, 4];  // All elements initialized to 0

// Initializing a 2D array with values
int[,] grid = new int[,]
{
    { 1, 2, 3, 4 },
    { 5, 6, 7, 8 },
    { 9, 10, 11, 12 }
};

// Accessing elements in a 2D array
int value = grid[1, 2];  // Row 1, Column 2: Value is 7
grid[0, 3] = 42;         // Set Row 0, Column 3 to 42

// Getting dimensions
int rows = grid.GetLength(0);      // 3
int columns = grid.GetLength(1);   // 4
int totalElements = grid.Length;   // 12 (3 * 4)

// Iterating through a 2D array
for (int i = 0; i < rows; i++)
{
    for (int j = 0; j < columns; j++)
    {
        Console.Write($"{grid[i, j],4}");  // The ,4 adds padding for alignment
    }
    Console.WriteLine();
}

// 3D array example
int[,,] cube = new int[3, 3, 3];  // 3x3x3 cube

// Set a value in the 3D array
cube[1, 2, 0] = 15;  // Layer 1, Row 2, Column 0

// Accessing and printing all elements in a 3D array
for (int i = 0; i < 3; i++)
{
    Console.WriteLine($"Layer {i}:");
    for (int j = 0; j < 3; j++)
    {
        for (int k = 0; k < 3; k++)
        {
            Console.Write($"{cube[i, j, k],4}");
        }
        Console.WriteLine();
    }
    Console.WriteLine();
}
                            

Jagged Arrays (Arrays of Arrays):


// Declaring a jagged array (an array of arrays)
int[][] jaggedArray = new int[3][];  // Array with 3 rows, each row can have different length

// Initializing the inner arrays
jaggedArray[0] = new int[4] { 1, 2, 3, 4 };        // First row has 4 elements
jaggedArray[1] = new int[2] { 5, 6 };              // Second row has 2 elements
jaggedArray[2] = new int[5] { 7, 8, 9, 10, 11 };   // Third row has 5 elements

// Alternative initialization syntax
int[][] jaggedArray2 = new int[][]
{
    new int[] { 1, 2, 3 },
    new int[] { 4, 5 },
    new int[] { 6, 7, 8, 9 }
};

// Accessing elements in a jagged array
int element = jaggedArray[1][1];  // Accessing row 1, column 1: Value is 6
jaggedArray[2][3] = 42;           // Setting row 2, column 3 to 42

// Iterating through a jagged array
for (int i = 0; i < jaggedArray.Length; i++)
{
    Console.Write($"Row {i}: ");
    
    for (int j = 0; j < jaggedArray[i].Length; j++)
    {
        Console.Write($"{jaggedArray[i][j]} ");
    }
    
    Console.WriteLine();
}

// Using nested foreach loops
foreach (int[] row in jaggedArray)
{
    foreach (int item in row)
    {
        Console.Write($"{item} ");
    }
    Console.WriteLine();
}
                            

Comparing multi-dimensional arrays:

Rectangular Arrays Jagged Arrays
All rows have the same length Rows can have different lengths
Use comma-separated indices: [i, j] Use multiple indices: [i][j]
More memory-efficient More flexible for irregular data
Slightly faster access Can allocate inner arrays as needed

Arrays as Parameters and Return Values

Arrays can be passed to methods and returned from methods, allowing for modular code that processes collections of data.

Arrays as Method Parameters:


// Passing an array to a method
void DisplayArray(int[] arr)
{
    Console.WriteLine("Array contents:");
    foreach (int item in arr)
    {
        Console.Write($"{item} ");
    }
    Console.WriteLine();
}

int[] numbers = { 1, 2, 3, 4, 5 };
DisplayArray(numbers);  // Pass the array to the method

// Modifying array elements in a method
void DoubleValues(int[] arr)
{
    for (int i = 0; i < arr.Length; i++)
    {
        arr[i] *= 2;  // Double each value
    }
}

DoubleValues(numbers);
DisplayArray(numbers);  // Numbers array is modified: 2 4 6 8 10

// Passing multidimensional arrays
void ProcessMatrix(int[,] matrix)
{
    int rows = matrix.GetLength(0);
    int cols = matrix.GetLength(1);
    
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            // Process each element
            matrix[i, j] += 10;
        }
    }
}

int[,] grid = { { 1, 2 }, { 3, 4 } };
ProcessMatrix(grid);  // Increases each element by 10
                            

Arrays as Return Values:


// Method that returns an array
int[] GenerateFibonacci(int count)
{
    if (count < 1) return new int[0];  // Return empty array for invalid input
    
    int[] fibonacci = new int[count];
    fibonacci[0] = 1;
    
    if (count > 1)
    {
        fibonacci[1] = 1;
        
        for (int i = 2; i < count; i++)
        {
            fibonacci[i] = fibonacci[i - 1] + fibonacci[i - 2];
        }
    }
    
    return fibonacci;
}

// Call the method and use the returned array
int[] fibSequence = GenerateFibonacci(8);
Console.WriteLine("Fibonacci sequence: " + string.Join(", ", fibSequence));
// Output: 1, 1, 2, 3, 5, 8, 13, 21

// Method that creates and returns a 2D array
int[,] CreateMultiplicationTable(int size)
{
    int[,] table = new int[size, size];
    
    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < size; j++)
        {
            table[i, j] = (i + 1) * (j + 1);
        }
    }
    
    return table;
}

// Use the returned 2D array
int[,] multiTable = CreateMultiplicationTable(5);
// Now multiTable contains a 5x5 multiplication table
                            

Important notes about arrays and methods:

  • Arrays are reference types, so methods receive a reference to the original array
  • Changes to array elements inside a method affect the original array
  • However, assigning a new array to the parameter doesn't affect the original reference
  • Use the params keyword for methods that accept a variable number of arguments

Using the params Keyword:


// Method that accepts variable number of arguments
int Sum(params int[] numbers)
{
    int total = 0;
    foreach (int number in numbers)
    {
        total += number;
    }
    return total;
}

// Call with individual arguments
int result1 = Sum(1, 2, 3);           // 6
int result2 = Sum(10, 20, 30, 40);    // 100
int result3 = Sum();                  // 0 (empty array)

// Call with an array
int[] values = { 5, 10, 15, 20 };
int result4 = Sum(values);            // 50
                            

Array Limitations and Alternatives

While arrays are fundamental data structures, they have some limitations. C# provides several alternative collection types that offer more flexibility.

Arrays vs. List<T>:


// Array - fixed size
int[] numbersArray = { 1, 2, 3, 4, 5 };
// Can't add elements to an array directly
// numbersArray.Add(6);  // Error! Arrays don't have an Add method

// List - flexible size
List numbersList = new List { 1, 2, 3, 4, 5 };
numbersList.Add(6);           // Add a single element
numbersList.AddRange(new[] { 7, 8, 9 });  // Add multiple elements
numbersList.Remove(3);        // Remove a specific element
numbersList.RemoveAt(0);      // Remove element at index 0

// Converting between arrays and lists
int[] arrayFromList = numbersList.ToArray();
List listFromArray = new List(numbersArray);
                            

Other Collection Types:


// Dictionary - key/value pairs
Dictionary ages = new Dictionary
{
    { "Alice", 25 },
    { "Bob", 30 },
    { "Charlie", 35 }
};

ages["David"] = 40;  // Add a new entry
ages["Alice"] = 26;  // Update an existing entry

if (ages.ContainsKey("Bob"))
{
    Console.WriteLine($"Bob's age: {ages["Bob"]}");
}

// HashSet - unique elements, no specific order
HashSet uniqueNames = new HashSet
{
    "Alice", "Bob", "Charlie"
};

uniqueNames.Add("Alice");  // No effect - "Alice" is already in the set
uniqueNames.Add("David");  // Adds "David" to the set

// Queue - first in, first out (FIFO)
Queue queue = new Queue();
queue.Enqueue("First");
queue.Enqueue("Second");
queue.Enqueue("Third");

string first = queue.Dequeue();  // Removes and returns "First"
string peek = queue.Peek();      // Returns "Second" without removing it

// Stack - last in, first out (LIFO)
Stack stack = new Stack();
stack.Push("First");
stack.Push("Second");
stack.Push("Third");

string top = stack.Pop();        // Removes and returns "Third"
string peekTop = stack.Peek();   // Returns "Second" without removing it

// LinkedList - doubly-linked list
LinkedList linkedList = new LinkedList();
linkedList.AddLast("End");
linkedList.AddFirst("Start");
linkedList.AddAfter(linkedList.First, "Middle");

foreach (string item in linkedList)
{
    Console.WriteLine(item);  // Start, Middle, End
}
                            

Collection selection guide:

Collection Type Best Used When
Array Fixed size, frequent indexed access, performance critical
List<T> Need to add/remove elements, unknown size upfront
Dictionary<TKey, TValue> Key-based lookup, associative data
HashSet<T> Need to maintain a set of unique values
Queue<T> First-in, first-out processing
Stack<T> Last-in, first-out processing
LinkedList<T> Frequent insertions/removals in the middle

LINQ with Arrays

Language Integrated Query (LINQ) provides a powerful way to query and transform arrays and other collections.

Common LINQ Operations with Arrays:


// Sample array
int[] numbers = { 5, 2, 9, 1, 7, 3, 8, 6, 4 };

// Filtering with Where
int[] evenNumbers = numbers.Where(n => n % 2 == 0).ToArray();
Console.WriteLine("Even numbers: " + string.Join(", ", evenNumbers));  // 2, 8, 6, 4

// Ordering with OrderBy/OrderByDescending
int[] sortedNumbers = numbers.OrderBy(n => n).ToArray();
Console.WriteLine("Sorted: " + string.Join(", ", sortedNumbers));  // 1, 2, 3, 4, 5, 6, 7, 8, 9

int[] descendingNumbers = numbers.OrderByDescending(n => n).ToArray();
Console.WriteLine("Descending: " + string.Join(", ", descendingNumbers));  // 9, 8, 7, 6, 5, 4, 3, 2, 1

// Transforming with Select
int[] doubledNumbers = numbers.Select(n => n * 2).ToArray();
Console.WriteLine("Doubled: " + string.Join(", ", doubledNumbers));

// Finding elements
int firstEven = numbers.First(n => n % 2 == 0);  // 2
int lastEven = numbers.Last(n => n % 2 == 0);    // 4
int firstOver10 = numbers.FirstOrDefault(n => n > 10);  // 0 (default for int) since none > 10

// Aggregation
int sum = numbers.Sum();                 // 45
int min = numbers.Min();                 // 1
int max = numbers.Max();                 // 9
double average = numbers.Average();      // 5.0
int product = numbers.Aggregate(1, (a, b) => a * b);  // Multiply all elements

// Checking conditions
bool allPositive = numbers.All(n => n > 0);     // true
bool anyEven = numbers.Any(n => n % 2 == 0);    // true
bool anyNegative = numbers.Any(n => n < 0);     // false

// Grouping
var groups = numbers.GroupBy(n => n % 3);  // Group by remainder when divided by 3
foreach (var group in groups)
{
    Console.WriteLine($"Numbers with remainder {group.Key} when divided by 3: {string.Join(", ", group)}");
}

// Taking and skipping elements
int[] firstThree = numbers.Take(3).ToArray();           // 5, 2, 9
int[] skipFirstTwo = numbers.Skip(2).ToArray();         // 9, 1, 7, 3, 8, 6, 4
int[] middle = numbers.Skip(3).Take(3).ToArray();       // 1, 7, 3

// Complex query with method chaining
var result = numbers
    .Where(n => n > 3)           // Filter: > 3
    .OrderBy(n => n)             // Sort ascending
    .Select(n => n * n)          // Transform: square each number
    .Take(3)                     // Take first 3 results
    .ToArray();                  // Convert to array

Console.WriteLine("Complex query result: " + string.Join(", ", result));  // 16, 25, 36
                            

Advantages of using LINQ with arrays:

  • Expressive, readable code for complex operations
  • Consistent API for working with different collection types
  • Deferred execution for many operations (better performance)
  • Composable queries for complex data transformations
  • Functional programming style with minimal state mutation