Unlock the Gift of the C# Spread Operator

This post is part of C# Advent! Be sure to check every day for new posts from the .NET community!

Happy Holidays, C# friends!

Last month, Microsoft announced many new features released as part of C# 12. The one I’m excited to show you today is the spread operator .. for collections.

As a polyglot, I was excited to see this as I’ve used the spread concept in other languages – including Ruby (splats * and **), Python (* for single-dimension collections, ** for dictionaries), and JavaScript.

So I was curious to see what I could do with it in C#.

What is the spread operator introduced in C# 12?

The spread operator .. introduced in C# 12 is used for splitting collections into individual items.

Hey! Isn’t that the range operator? That too! We’ll see it in action here later with some helpful comments to explain how to differentiate between the meanings.

Since this element applies to a variety of collections, I’m going to create a function to print a collection as long as it implements IEnumerable. This way we can look at values of our new collections.

using System.Collections;
// Display the collection
void PrintCollection(IEnumerable collection){
    foreach (var item in collection){
        Console.WriteLine(item);
    }
    Console.WriteLine("================");
}

Collection Expressions

Collection expressions are also new as of C# 12. These allow us to declare our collections using a consistent syntax to express our sequence of elements, surrounded by square brackets. This applies to many different collections, including (but not limited to):

  • Arrays
  • Span<T> and ReadOnlySpan<T>
  • Any type with a collection initializer
  • … and more! See the link I included for the list!

Collection expressions must have a type declaration and cannot be inferred.

For example, try this:

var baumDeer = ["Flossie","Glossie","Racer","Pacer","Reckless","Speckless","Fearless","Peerless","Ready","Steady"];

You will see the following error:

There is no target type for the collection expression.

So no var with collection expressions. You must declare the target type.

Spread-ing Various Collections in C# 12

Let’s look at how we can apply the spread operator to collections.

Arrays

The first array we’ll look at is the int[] array. First, let’s look at .. as a range operator, as seen in this code that declares an integer array and grabs the element as index 0 through index 3, exclusive:

// C# range operator - .. - only applies to *indices*
int[] daysOfChristmas = [1,2,3,4,5,6,7,8,9,10,11,12];
var firstFewDays = daysOfChristmas[0..3];
PrintCollection(firstFewDays);

The output is:

1
2
3
================

Now let’s look at .. in terms of spreading elements. The code will:

  1. Use the range operator to get the first two elements.
  2. Use the range operator to get the last few elements – from index 8 until the end.
  3. Create a new array that uses the spread operator to break out the individual elements from each of the collections from steps 1 and 2.

Here is the code:

// This is a range operator - from start until the 2nd item, exclusive
var firstTwoDays = daysOfChristmas[..2];
// First: Range operator - 8th element until the end
var lastFewDays = daysOfChristmas[8..];
// You can do a lot with ranges - see the documentation for more guidance. 
// Now as the spread operator with collection expressions
int[] fewDaysOnEitherSide = [..firstTwoDays, ..lastFewDays];
PrintCollection(fewDaysOnEitherSide);

This is the output:

1
2
9
10
11
12
================

This also works with other arrays, including inline arrays. I’ve got one more arrays example – showing board games that my kids enjoy playing during our annual holiday gaming party and other games we play as a family.

In this part, we continue to use the collection expressions to initialize our arrays.

// See how this works as the spread operator for a string[]
string[] kidsGames = ["Dixit","Zombie Fluxx","Sushi Go","Hive"];
string[] familyGames = ["Takenoko","Barenpark","5-Minute Dungeon"];
string[] allGames = [.. kidsGames, .. familyGames];
PrintCollection(allGames);

Span

We can’t use PrintCollection() with Span<T> since its Enumerate() does not implement IEnumerable. So this is the code we’ll start with:

// With Span - can't use the IEnumerator method because its Enumerator is a ref struct
Span<string> reindeer = ["Dasher","Dancer","Prancer","Vixen","Comet","Cupid","Donder","Blitzen"];
foreach (var deer in reindeer){
    Console.Write($"{deer}, ");
}
Console.WriteLine("");

This is the output:

Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donder, Blitzen,

Oh no! We forgot Rudolph. Let’s put our reindeer in a collection and use the .. spread operator to break out the individual elements from the Span.

string[] completedReindeer = [..reindeer, "Rudolph"];
PrintCollection(completedReindeer);

The deer output looks like this:

Dasher
Dancer
Prancer
Vixen
Comet
Cupid
Donder
Blitzen
Rudolph
================

Collections

So far, we’ve used .. to spread operators of arrays and Span. Now, let’s look at it in a collection.

For this example, we’re going to add L. Frank Baum’s reindeer from The Life and Adventures of Santa Claus.

List<string> baumDeer = ["Flossie","Glossie","Racer","Pacer","Reckless","Speckless","Fearless","Peerless","Ready","Steady"];
// Spreading a string[] and a List<string>
string[] reindeerSquad = [..completedReindeer,..baumDeer];
PrintCollection(reindeerSquad);

This prints each of the reindeer in our collection.

BONUS: Primary Constructors

Primary constructors are another feature of C# 12.

Suppose we want to track our reindeer by name and nose color. Let’s use this Reindeer class:

// Reindeer class
public class Reindeer (string name, string noseColor = "brown") {
    public override string ToString(){
        return$"Reindeer: {name}\tNose color: {noseColor}";
    }
}

This is using the primary constructor notation. This means that there will not be a Reindeer() default constructor. The only way to create an instance of Reindeer is to pass in 1 string with an optional 2nd string that defaults to "brown".

Suppose we wanted to create a list of our reindeer using this Reindeer class. Check out this code:

foreach (var deer1 in allReindeer){
    // Could be a ternary// Written this way for readability
    if (deer1 == "Rudolph"){
        reindeerSquad.Add(new(deer1,"red"));
    } else {
        reindeerSquad.Add(new(deer1));
    }
}
PrintCollection(reindeerSquad);

The output would be:

Reindeer: Dasher        Nose color: brown
Reindeer: Dancer        Nose color: brown
Reindeer: Prancer       Nose color: brown
Reindeer: Vixen Nose color: brown
Reindeer: Comet Nose color: brown
Reindeer: Cupid Nose color: brown
Reindeer: Donder        Nose color: brown
Reindeer: Blitzen       Nose color: brown
Reindeer: Rudolph       Nose color: red
Reindeer: Flossie       Nose color: brown
Reindeer: Glossie       Nose color: brown
Reindeer: Racer Nose color: brown
Reindeer: Pacer Nose color: brown
Reindeer: Reckless      Nose color: brown
Reindeer: Speckless     Nose color: brown
Reindeer: Fearless      Nose color: brown
Reindeer: Peerless      Nose color: brown
Reindeer: Ready Nose color: brown
Reindeer: Steady        Nose color: brown
================

Now, suppose we want a new reindeer squad made up of the first few and last few reindeer. Let’s use the .. as a range to split them then as the spread operator to combine the first few and last few collections.

// This is a range operator - from start until the 2nd item, exclusive
var firstCoupleReindeer = reindeerSquad[..2];
// Last 2 deer
var lastFewReindeer = reindeerSquad[^2..];
// Now as the spread element with collection expressions
List<Reindeer> newReindeerSquad = [..firstCoupleReindeer, ..lastFewReindeer];
PrintCollection(newReindeerSquad);

This is the new reindeer squad:

Reindeer: Dasher        Nose color: brown
Reindeer: Dancer        Nose color: brown
Reindeer: Ready Nose color: brown
Reindeer: Steady        Nose color: brown
================

Conclusion

In this post, we explored the new spread operator for collections .. introduced in C# 12. Check out more C# gifts throughout this month as they are unlocked on the C# Advent Calendar.

By sadukie

One thought on “Unlock the Gift of the C# Spread Operator”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.