Collections have been a part of .NET since the very beginning, and there have been both incremental and significant enhancements since then. Likewise C# has evolved to improve how it works with collections.
Consider an array of strings like this:
string[] days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
Not something we always think about, but it’s a good practice to set the capacity of collections if you know ahead of time how large they need to be. Memory can be allocated up front, rather than incrementally (and depending on the kind of collection it may also avoid needing to reallocate a larger data structure as it grows).
Up until now, you might do that like this for a list that you know will eventually need to hold 7 items, even though initially you’re creating it with 3.
var simpleDayList1 = new List<string>(days.Length);
simpleDayList1.AddRange(days[0..3]);
Console.WriteLine($"Capacity of simpleDayList1: {simpleDayList1.Capacity}, Count: {simpleDayList1.Count}");
The Capacity will be 7.
But what if you’re using collection expressions? Collection expressions are when you use [] to create a collection. The first array above is using them.
Now with C# 15 collection expression arguments, you can include a with() element as part of the collection expression to set constructor arguments, like this:
List<string> dayList = [with(capacity: days.Length), ..days[0..3]];
Console.WriteLine($"Capacity of dayList: {dayList.Capacity}, Count: {dayList.Count}");
The result is the same, but with a more compact syntax.
One thing that confused me at first - I couldn’t figure out how that code was working, as there are 3 constructors defined for List<T> and none of them take a capacity and the items.
I used dotPeek to disassemble the compiled code and this is what it gave me (I had to delete the .pdb otherwise dotPeek was too clever and just showed me the original code!):
List<string> equivalentDayList = new List<string>(days.Length);
equivalentDayList.AddRange((IEnumerable<string>)days[0..3]);
It chose the List<T>(int capacity) constructor. The conversions reference suggests it will call the Add() method to add the items to the collection. I assume there’s an optimisation that will use AddRange() if it is available.
Conclusion
Collection expression arguments are a nice ‘syntactic sugar’ incremental improvement to C#’s support for collections.
