C# collection expressions provide a concise syntax for creating common collection values with square brackets. Introduced in C# 12, they can initialize arrays, lists, spans, and other supported collection types without repeating constructor or type syntax.
Collection expressions also support the spread element, written with two dots, so values from an existing collection can be inserted into a new collection.
Collection Expression Syntax
// The target type tells the compiler what collection to create.
int[] numbers = [10, 20, 30];
List<string> names = ["Asha", "Mia", "Noah"];
A collection expression does not have a standalone natural type. The compiler uses the surrounding target type to decide what collection should be created.
Because a collection expression needs target typing, code such as var numbers = [1, 2, 3]; is not valid. Specify a compatible target type instead.
Create Arrays and Lists
The following example creates an array, copies its elements into another array with a spread element, and initializes a List directly.
Example:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
int[] first = [72, 88];
int[] all = [.. first, 95, 91];
List<string> steps = ["Design", "Build", "Test"];
Console.WriteLine(string.Join(", ", all));
Console.WriteLine(string.Join(" -> ", steps));
}
}
Output:
72, 88, 95, 91
Design -> Build -> Test
Use the Spread Element
A spread element starts with .. followed by an expression that can be enumerated. Its values are inserted into the surrounding collection expression.
Example:
int[] morning = [8, 9];
int[] afternoon = [13, 14];
// Combine existing values with new values.
int[] schedule = [7, .. morning, 12, .. afternoon, 17];
Console.WriteLine(string.Join(", ", schedule));
Output:
7, 8, 9, 12, 13, 14, 17
The spread element is different from the range operator even though both use two dots. Inside a collection expression, ..source means insert the source elements into the new collection.
Create Empty Collections
An empty collection expression is simply []. The target type still determines what object is produced.
string[] tags = [];
List<int> scores = [];
Use Collection Expressions with Spans
Collection expressions can target Span<T> and ReadOnlySpan<T> as well as arrays and List<T>.
ReadOnlySpan<char> vowels = ['a', 'e', 'i', 'o', 'u'];
foreach (char letter in vowels)
{
Console.Write(letter + " ");
}
Target Typing
The same collection expression can produce different supported collection shapes depending on the target context.
| Target | Example |
|---|---|
| Array | int[] values = [1, 2, 3]; |
| List | List<int> values = [1, 2, 3]; |
| Span | Span<int> values = [1, 2, 3]; |
| ReadOnlySpan | ReadOnlySpan<int> values = [1, 2, 3]; |
Target typing also works in method arguments when the parameter type provides the required context.
Example:
static void PrintNames(List<string> names)
{
Console.WriteLine(string.Join(", ", names));
}
// The parameter type supplies the target type.
PrintNames(["Riya", "Oliver", "Mia"]);
Nested Collection Expressions
You can use collection expressions inside another expression when the element type is itself a supported collection type.
int[][] grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
This form is useful for jagged arrays and other nested data structures where the target type makes each inner collection clear.
Common Mistakes
- Using var without a target type: collection expressions need enough context to determine the resulting collection type.
- Confusing spread with ranges: ..source inserts elements; it does not select a numeric or index range.
- Assuming every custom collection works automatically: a type must match one of the supported collection construction patterns.
- Spreading null: the spread source must be a valid enumerable value at runtime.
When to Use Collection Expressions
- Initialize short arrays and lists with less repeated syntax.
- Combine existing sequences with additional values using spread elements.
- Create empty collections where the target type is already obvious.
- Pass small collection values directly to methods with compatible parameter types.
Best Practices
- Keep the target type visible when it improves readability.
- Use spread elements for clear composition rather than manually copying items.
- Avoid very large inline expressions when named intermediate collections would be easier to understand.
- Choose the target collection type according to behavior and performance needs, not only the shortest syntax.
Conclusion
C# collection expressions make common collection creation shorter and more consistent. Use square brackets with an explicit or contextual target type, add spread elements when you need to combine sequences, and keep the resulting collection type clear to readers. The feature reduces initialization boilerplate while preserving normal array, list, and span behavior.