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>
andReadOnlySpan<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:
- Use the range operator to get the first two elements.
- Use the range operator to get the last few elements – from index 8 until the end.
- 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.
[…] Unlock the Gift of the C# Spread Operator (Sarah Dukiewicz) […]