Back to Blog
C#

C# Collection Expression vs Collection Initializer

c# collection expression vs collection initializer: Compare C# collection expressions with collection initializers: syntax, type inference, span support, and when to u...

C#Collection ExpressionsCollection InitializersC# 12.NET
Illustration comparing C# collection expression syntax with collection initializer syntax showing two code paths converging on a single collection.

When you initialize a collection in C#, the collection initializer has been the standard syntax since C# 3.0. The collection expression, introduced in C# 12, offers a shorter form and changes how the compiler resolves the target type. Understanding c# collection expression vs collection initializer matters when you choose which syntax to adopt in new code and whether to migrate existing initialization sites.

The Two Syntaxes Side by Side

A collection initializer creates a new instance and populates it with elements:

List<int> numbers = new List<int> { 1, 2, 3 };

The collection expression removes the explicit constructor call:

List<int> numbers = [1, 2, 3];

The compiler infers the collection type from the variable declaration. The same expression can target an array, a list, a span, or an interface:

int[] array = [1, 2, 3]; List<int> list = [1, 2, 3]; Span<int> span = [1, 2, 3]; IEnumerable<int> sequence = [1, 2, 3];

A collection initializer cannot target Span<int> or IEnumerable<int> directly because it requires a concrete type with an accessible Add method. The collection expression works with any type the compiler recognizes as a collection, which includes arrays, spans, and types that implement IEnumerable<T> with a suitable builder.

How Type Inference Differs

Collection initializers always require an explicit target type because the syntax is tied to the constructor call. Collection expressions are target-typed: the compiler determines the concrete type from the assignment context. This makes them usable in return statements, method arguments, and switch expressions where a collection initializer would need a temporary variable.

static int[] BuildValues() => [10, 20, 30];

The target-typed nature also means the same literal can be passed directly to a method parameter without an intermediate variable. This reduces noise in call sites that previously required a local collection.

Using the Spread Operator to Combine Collections

Collection expressions introduce the spread operator .., which expands an existing collection into the new one:

int[] first = [1, 2]; int[] second = [3, 4]; int[] combined = [.. first, .. second, 5];

The resulting array contains 1, 2, 3, 4, 5. The spread operator works with any enumerable source, including LINQ results, and the compiler copies the elements into the target collection. A collection initializer has no equivalent syntax; combining collections required calling AddRange or looping over the source.

Span Support and Allocation Behavior

A significant difference between the two syntaxes is how they interact with stack-allocated memory. A collection expression can target Span<T> and ReadOnlySpan<T>:

Span<int> values = [1, 2, 3];

The compiler emits code that builds the span without a heap allocation when the element count is known at compile time. A collection initializer cannot produce a span at all, because it depends on instance construction and Add calls. If you need a temporary buffer for a short-lived operation, the collection expression avoids allocating a List<int> or array on the managed heap. The exact allocation behavior depends on the target type and the compiler's ability to inline the construction, so it is worth measuring in a hot path rather than assuming a guarantee.

Compatibility and Language Version Requirements

Collection expressions require C# 12 or later. The syntax itself is a compiler feature, so it works with older .NET runtimes for most target types. Span targets, however, depend on System.Span<T>, which requires .NET Core 2.1+ or the System.Memory package on .NET Framework. Collection initializers work in every C# version since 3.0 and have no runtime dependency beyond the type's Add method.

When a codebase targets multiple language versions, the project file's <LangVersion> setting controls whether collection expressions compile. A team standardizing on C# 12 can adopt the new syntax, while a library that must compile under older language versions should keep collection initializers.

Choosing Between Collection Expression and Collection Initializer

Use a collection expression when the target type is an array, span, or a common collection type such as List<T>, and when the element set is known at the call site. The syntax is shorter, works in more contexts, and supports the spread operator.

Keep the collection initializer when you initialize a custom type that implements IEnumerable and exposes an Add method but does not qualify as a collection expression target. Custom collection types need a CollectionBuilder attribute to work with collection expressions; without it, the compiler rejects the literal. For example, a custom NameCollection with an Add(string) method continues to rely on the initializer syntax unless you add the builder attribute.

The decision also depends on the team's language version policy. If the project compiles with an older language version, collection initializers remain the portable choice. If the project already uses C# 12, collection expressions reduce boilerplate and make collection construction consistent across arrays, lists, and spans.

c# collection expression vs collection initializer: Practica | RYUSLOG DEV