Introduction

In modern C#, collection expressions, range, and spread operators offer a concise way to work with collections such as lists, hashsets, and arrays. This article explores these new syntax features, from primary constructors through collection expressions to the range and spread operators.

Let's start with the primary constructor

The primary constructor is utilized to simplify the initialization of classes. Now, you can declare the constructor directly in the class declaration, reducing boilerplate code. Here's how its syntax looks:

public class Person(string name, int age)
{
    public string Name { get; } = name;
    public int Age { get; } = age;
}

//Initialization of class Person
List<Person> people = new()
{
    new Person("Alice", 30),
    new Person("Bob", 25),
    new Person("Charlie", 35)
};

Listing 1. Class Person with the primary constructor.

Collection expressions

What you're seeing in Listing 1 is the traditional way of initializing a list. Let me show you the new, modern, and simpler way to initialize a list using collection expressions.

List<Person> people =
[
     new Person("Alice", 30),
     new Person("Bob", 25),
     new Person("Charlie", 35)
];

Listing 2. Initializing a list with collection expression.

A developer with common sense would consider this a joke, right? "Whoa, Microsoft changed curly braces {..} into square bracket [..], BIG WHOOP". That's what I thought, too, until Pandora's shit box was opened. Have a look at the following syntax. It shows collection expressions on other collections. That's pretty sweet! Look at that consistency.

List<int> list = [1, 2, 3, 4, 5];
HashSet<string> set = ["apple", "banana", "cherry"];

int[] ints = [1, 2, 3, 4, 5];
int[][] jaggedArray =
[
    [1, 2, 3],
    [4, 5],
    [6, 7, 8, 9]
];

Listing 3. Initializing collections with collection expressions.

The Range Operator [..]

The range operator [2..5] is utilized to specify a range between two indices in an array or a string. It provides a concise method to slice arrays and strings without the need for explicit loops or Array.Copy methods.

Note. The startIndex is inclusive, and the endIndex is exclusive.

Let me walk you through examples of each to give you a better understanding.

Let's start with the first one,

[startIndex..endIndex] - [2..5]

int[] arr = [1, 2, 3, 4, 5, 6];
int[] result = arr[2..5]; //Output: 3, 4, 5

[startIndex ..] - [2..]

int[] arr = [1, 2, 3, 4, 5, 6];
int[] result = arr[2..]; //Output: 3, 4, 5, 6

[startIndex..] : [..5]

int[] arr = [1, 2, 3, 4, 5, 6];
int[] result = arr[..5];//Output: 1, 2, 3, 4, 5

Range [..]

int[] arr = [1, 2, 3, 4, 5, 6];
int[] result = arr[..];//Output: 1, 2, 3, 4, 5, 6

^1 Operator

int[] arr = [1, 2, 3, 4, 5, 6];
int result = arr[^1];//Output: 6

Range [ .. ] with ^ - [ ^3 .. ]

int[] arr = [1, 2, 3, 4, 5, 6];
int[] result = arr[^3..];//Output 4, 5, 6

Range [ .. ] with ^ - [ .. ^3 ]

int[] arr = [1, 2, 3, 4, 5, 6];
int[] result = arr[..^3];//Output: 1, 2, 3

Range [ .. ] with ^ - [ ^3 .. ^1 ]

int[] arr = [1, 2, 3, 4, 5, 6];
int[] result = arr[^3..^1];//Output: 4, 5

Cloaning Arrays

int[] inputArray = [1, 2, 3, 4, 5, 6];
int[] cloneArray1 = [..inputArray]; //Output: 1, 2, 3, 4, 5, 6
int[] cloneArray2 = inputArray[..]; //Output: 1, 2, 3, 4, 5, 6

You can clone an array using either of the following methods.

Both cloneArray1 and cloneArray2 will contain the same elements as inputArray, creating clones of the original array.

Merging Arrays

int[] first = [1, 2, 3, 4, 5, 6];
int[] second = [7, 8, 9, 10];

int[] mergedArray = [..first, ..second];//output: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

int[] mergedArray = [..first, ..second]

Substring Extraction with the Range Operator

string greeting = "Hello, World!";
string helloSubstring = greeting[..5]; // "Hello"
string worldSubstring = greeting[7..12]; // "World"
string worldExclamationSubstring = greeting[7..]; // "World!"

Extracting Substrings

greeting[..5]

greeting[7..12]

greeting[7..]

Sure, here's a small tip

Conclusion

These collection expressions and slicing in C# open up a consistent way of initializing collections for developers. This code is concise and maintainable. Understanding the range operator helps a lot in substring and array operations. It might be slightly confusing at the beginning, but once you get a grip on it, it's easier than ever. Hope you enjoyed this article. See you soon.