Lesson 1
Understanding Arrays in C#
Understanding Arrays in C#

In today's lesson, we'll explore arrays in C#, a versatile and fundamental data structure. An array is an ordered collection of elements that can be of mixed data types. Arrays in C# are mutable, meaning their elements can be changed after creation. However, arrays can be made immutable using certain techniques if needed.

The beauty of arrays lies in their simplicity and efficiency; they allow for easy storage, access, and manipulation of data. By the end of this lesson, you'll be able to create, manipulate, and understand the unique applications of arrays in C#.

Creating Arrays

Arrays are a fundamental part of C# programming, allowing you to define collections of elements of the same type. There are two primary ways to create arrays in C#: using array literals and using constructors with the new keyword.

  1. Array Literals: An array can be declared and initialized with specific values directly using an array literal. This involves enclosing the elements within curly braces {} without using the new keyword.

  2. Using Constructors: Arrays can also be created using the new keyword followed by the type, square brackets [], and the elements enclosed in curly brackets {}. This approach explicitly specifies the array's type and initializes it with values.

It is considered best practice to declare arrays with readonly in C# when the reference to the array itself should not change, but the contents can.

In the next C# example, we illustrate array creation using both array literals and constructors:

C#
1using System; 2 3class ArrayExample 4{ 5 public (string[] arrayLiteral, string[] fromConstructor) CreateArrays() 6 { 7 string[] arrayLiteral = { "apple", "banana", "cherry" }; // Using array literals 8 string[] fromConstructor = new string[] { "apple", "banana", "cherry" }; // Using constructor 9 return (arrayLiteral, fromConstructor); 10 } 11} 12 13class Program 14{ 15 static void Main() 16 { 17 var arrayExample = new ArrayExample(); 18 var arrays = arrayExample.CreateArrays(); 19 Console.WriteLine(string.Join(", ", arrays.arrayLiteral)); // Output: apple, banana, cherry 20 Console.WriteLine(string.Join(", ", arrays.fromConstructor)); // Output: apple, banana, cherry 21 } 22}

This example demonstrates how to create arrays using both the array literal method and the constructor method, providing the same resulting arrays in both cases.

Understanding Arrays

An array in C# is an ordered collection of elements, which can be of any type, including numbers, strings, objects, and even other arrays.

Consider this C# array declaration as an example:

C#
1using System; 2 3class ArrayExample 4{ 5 public object[] CreateMixedArray() 6 { 7 return new object[] { "apple", 42, true, new { name = "banana" }, new int[] { 1, 2, 3 } }; 8 } 9} 10 11class Program 12{ 13 static void Main() 14 { 15 var arrayExample = new ArrayExample(); 16 var mixedArray = arrayExample.CreateMixedArray(); 17 foreach (var element in mixedArray) 18 { 19 Console.WriteLine(element); 20 } 21 // Output: 22 // apple 23 // 42 24 // True 25 // { name = banana } 26 // System.Int32[] 27 } 28}
Inspecting and Modifying Arrays

Just like strings, array elements are accessible via indexes. Indexes in C# arrays are zero-based, meaning the first element has an index of 0, the second element has an index of 1, and so on. C# also introduces the ^ symbol, which allows accessing elements from the end of the array. For example, ^1 refers to the last element, ^2 to the second-to-last, and so on.

The Length property of an array returns the number of elements present in the array and can be used to easily determine its size or iterate over the array. Though arrays are mutable by default, you can create new arrays from existing ones or use Array.AsReadOnly() to enforce immutability.

Consider the following example that depicts inspecting, modifying arrays, and demonstrating the Length property:

C#
1using System; 2using System.Collections.ObjectModel; 3 4class ArrayExample 5{ 6 public void InspectArray() 7 { 8 string[] myArray = { "apple", "banana", "cherry", "durian", "elderberry" }; 9 Console.WriteLine(myArray[1]); // Output: banana 10 Console.WriteLine(myArray[^1]); // Output: elderberry 11 12 string[] slice = new string[2]; 13 Array.Copy(myArray, 2, slice, 0, 2); 14 Console.WriteLine(string.Join(", ", slice)); // Output: cherry, durian 15 16 string[] newArray = { myArray[1], myArray[2], "dragonfruit" }; 17 Console.WriteLine(string.Join(", ", newArray)); // Output: banana, cherry, dragonfruit 18 19 ReadOnlyCollection<string> readOnlyArray = Array.AsReadOnly(newArray); 20 21 // Demonstrating the behavior that causes an exception 22 // Uncomment the following line to see the runtime exception 23 // readOnlyArray[0] = "blueberry"; 24 25 Console.WriteLine(string.Join(", ", readOnlyArray)); // Output: banana, cherry, dragonfruit 26 } 27} 28 29class Program 30{ 31 static void Main() 32 { 33 var arrayExample = new ArrayExample(); 34 arrayExample.InspectArray(); 35 } 36}

Here's a deeper dive into some of the array methods and features used:

  1. ^ symbol allows accessing array elements from the end, with ^1 referring to the last element.
  2. Array.Copy(myArray, 2, slice, 0, 2) copies a range of elements from one array to another, in this example, it copies 2 elements from myArray, starting at index 2, to the slice array, starting at index 0.
  3. Length is a property that gives the total number of elements in the array.
  4. Array.AsReadOnly(array) returns a read-only wrapper for the specified array.
Operations on Arrays

C# arrays allow a range of operations. LINQ (Language Integrated Query) is a powerful feature in C# that provides query capabilities directly in the C# language, enabling more concise and readable code when performing operations on collections, such as arrays. Here's an overview of some common operations using LINQ on arrays:

The Concat() method using LINQ concatenates two arrays, while loops or the Enumerable.Repeat() method can be used to repeat elements of an array. The Contains() method checks if a particular item is present in the array. Additionally, the Select() method using LINQ creates a new array populated with the results of calling a provided function on every element in the array.

Here's an example demonstrating these operations:

C#
1using System; 2using System.Linq; 3 4class ArrayExample 5{ 6 public void ArrayOperations() 7 { 8 string[] array1 = { "apple", "banana" }; 9 string[] array2 = { "cherry", "durian" }; 10 var array3 = array1.Concat(array2).ToArray(); 11 Console.WriteLine(string.Join(", ", array3)); // Output: apple, banana, cherry, durian 12 13 var array4 = Enumerable.Repeat(array1, 3).SelectMany(e => e).ToArray(); 14 Console.WriteLine(string.Join(", ", array4)); // Output: apple, banana, apple, banana, apple, banana 15 16 Console.WriteLine(array1.Contains("apple")); // Output: True 17 18 var mappedArray = array1.Select(fruit => $"{fruit} pie").ToArray(); 19 Console.WriteLine(string.Join(", ", mappedArray)); // Output: apple pie, banana pie 20 } 21} 22 23class Program 24{ 25 static void Main() 26 { 27 var arrayExample = new ArrayExample(); 28 arrayExample.ArrayOperations(); 29 } 30}

Here’s a breakdown of the array methods used:

  1. Concat(anotherArray) from LINQ returns a new array that is the result of joining two or more arrays.
  2. Enumerable.Repeat(value, n) creates a sequence with n copies of the value.
  3. SelectMany(e => e).ToArray() is used to flatten a sequence of sequences into a single sequence. In this example, it flattens the repeated sequences into one continuous array.
  4. Contains(element) checks if the array includes a certain element and returns true or false.
  5. Select(callback) from LINQ creates a new array populated with the results of calling a provided function on every element in the array. In this case, we use a lambda function fruit => $"{fruit} pie" for concise syntax.
Nested Arrays

An array can contain another array, resulting in a nested array. In a nested array, you can access elements by chaining indices. The first index accesses an element within the top-level array, and the subsequent index(es) access elements within the sub-array. Here's an example of creating and indexing a nested array:

C#
1using System; 2 3class ArrayExample 4{ 5 public object[] CreateNestedArray() 6 { 7 return new object[] { "apple", new object[] { "banana", "cherry" } }; 8 } 9} 10 11class Program 12{ 13 static void Main() 14 { 15 var arrayExample = new ArrayExample(); 16 var nestedArray = arrayExample.CreateNestedArray(); 17 Console.WriteLine(nestedArray[0]); // Output: apple 18 Console.WriteLine(nestedArray[1]); // Output: System.Object[] 19 20 var subArray = (object[])nestedArray[1]; 21 Console.WriteLine(subArray[0]); // Output: banana 22 Console.WriteLine(subArray[1]); // Output: cherry 23 } 24}

In this example, the nestedArray contains a mix of strings and another array. The line var subArray = (object[])nestedArray[1]; is performing an explicit type casting. The element at nestedArray[1] is actually an array of object, and by casting it to object[], we can safely access its elements using indices. Explicit type casting is necessary here as the C# compiler needs an assurance of the type before accessing elements, especially when dealing with heterogeneous collections like arrays of object.

Array destructuring isn't as straightforward in C# as it is in JavaScript, but you can manually extract values by indexing. For more complex destructuring scenarios, you might need to use tuples or custom objects.

Complex Nested Arrays and Advanced Concepts

You can create complex nested arrays where each element can be an object containing arrays. Consider the following example with car manufacturers and their models:

C#
1using System; 2 3class Car 4{ 5 public string Name { get; set; } = string.Empty; 6 public string[] Models { get; set; } = Array.Empty<string>(); 7} 8 9class ArrayExample 10{ 11 public Car[] CreateNestedObjectArray() 12 { 13 return new Car[] 14 { 15 new Car { Name = "Ford", Models = new string[] { "Fiesta", "Focus", "Mustang" } }, 16 new Car { Name = "BMW", Models = new string[] { "320", "X3", "X5" } }, 17 new Car { Name = "Fiat", Models = new string[] { "500", "Panda" } } 18 }; 19 } 20} 21 22class Program 23{ 24 static void Main() 25 { 26 var arrayExample = new ArrayExample(); 27 var cars = arrayExample.CreateNestedObjectArray(); 28 foreach (var car in cars) 29 { 30 Console.WriteLine($"{car.Name}: {string.Join(", ", car.Models)}"); 31 } 32 33 // Access elements in the nested object array 34 Console.WriteLine(string.Join(", ", cars[0].Models)); // Output: Fiesta, Focus, Mustang 35 Console.WriteLine(cars[1].Name); // Output: BMW 36 Console.WriteLine(cars[2].Models[1]); // Output: Panda 37 } 38}

This demonstrates how nested arrays can be used in complex structures, making it easier to organize and access data.

Lesson Summary

Excellent job! In this lesson, you've learned what arrays are and how to create, inspect, and operate on them. You've also learned some advanced concepts like nested arrays and destructuring.

Going forward, our focus will be on meticulously designed practice exercises that solidify your understanding. Remember, the key to successful learning is practice. Reinvent these examples by breaking and fixing them again. Let's dive deeper!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.