C# 15 Collection Expression Arguments: Pass Constructors Inline with with(...)
C# 15 adds the with(...) element to collection expressions, letting you pass capacity, comparers, and other constructor arguments directly in the initializer.
This site uses Walter, an AI agent, for research, writing, and maintenance. Editorial and verification process
Collection expressions landed in C# 12 and have been absorbing new capabilities ever since. C# 15, shipping with .NET 11, adds a missing piece: you can now pass arguments to the collection’s constructor or factory method with a with(...) element placed at the start of the expression.
Why this matters
Before C# 15, collection expressions inferred the target type and called its default constructor. If you needed a case-insensitive HashSet<string> or a List<T> pre-sized for a known capacity, you had to fall back to a traditional initializer or a two-step setup:
// C# 14 and earlier: no way to pass a comparer via collection expression
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Hello", "HELLO" };
// Or the awkward two-step
List<string> names = new(capacity: 100);
names.AddRange(source);
Both patterns break the concise flow that collection expressions were designed for.
Inline constructor arguments with with(...)
C# 15 lets you write this instead:
string[] values = ["one", "two", "three"];
// Pre-allocate capacity
List<string> names = [with(capacity: values.Length * 2), .. values];
// Case-insensitive set in a single expression
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// set.Count == 1
The with(...) element must appear first. After it, the rest of the expression works exactly like any other collection expression: literals, spreads, and nested expressions all compose normally.
Dictionaries get the same treatment
The feature really shines with Dictionary<TKey, TValue>, where comparers are common but previously forced you off collection expressions entirely:
Dictionary<string, int> headers = [
with(StringComparer.OrdinalIgnoreCase),
KeyValuePair.Create("Content-Length", 512),
KeyValuePair.Create("content-length", 1024) // overwrites the first entry
];
// headers.Count == 1
Without with(...), you could not pass a comparer through a collection expression at all. The only option was a constructor call followed by manual adds.
Constraints to know
A few rules to keep in mind:
with(...)must be the first element in the expression.- It is not supported on arrays or span types (
Span<T>,ReadOnlySpan<T>), since those don’t have constructors with configuration parameters. - Arguments cannot have
dynamictype.
A natural evolution
C# 12 gave us the syntax. C# 13 extended params to accept collection expressions. C# 14 broadened implicit span conversions. Now C# 15 removes the last common reason to abandon collection expressions: constructor configuration. If you are already on .NET 11 Preview 2 or later, you can try this today with <LangVersion>preview</LangVersion> in your project file.
Full spec: Collection expression arguments proposal.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.