Skip to main content
The slice pattern (..) is a subpattern used within C# list patterns ([...]) to match zero or more elements in a collection. It enables structural matching of sequences by asserting the positions of specific elements while abstracting away the length and content of the remaining sequence.

Syntax and Mechanics

The slice pattern is denoted by two periods (..). It operates under strict positional evaluation rules:
  1. Single Instance Constraint: A single list pattern can contain a maximum of one slice pattern.
  2. Zero-or-More Evaluation: The slice pattern successfully matches even if there are zero elements available to fill the slice.
  3. Pattern Application: The slice pattern is not limited to being a discard or a variable capture. It can be followed by any pattern (such as a property pattern, a list pattern, or a var pattern) to validate the shape, length, or contents of the matched sub-sequence.

Syntax Visualization

1. Discarding Elements

When used alone, the slice pattern acts as a discard for a sequence of elements.

2. Capturing Elements

When combined with a var declaration or a specific type, the slice pattern captures the matched elements into a new variable.

3. Applying Sub-patterns

The slice pattern can be followed by another pattern to evaluate the matched sub-sequence without necessarily capturing it.

4. Zero-Element Matching

The slice pattern dynamically adjusts to the length of the sequence. If the explicit positional patterns consume the entire sequence, the slice pattern resolves to an empty collection.

Static Type Resolution Rules

To use a list pattern at all, a type must be countable (possessing an accessible Count or Length property) and indexable (possessing an accessible int indexer). However, when capturing a slice (e.g., .. var slice) or applying a sub-pattern to it (e.g., .. { Length: 3 }), the type must additionally be “sliceable”. The type of the matched sub-sequence is determined strictly at compile time based on the static type of the collection being matched. The compiler evaluates the following criteria to extract the slice:
  1. It is an array type (T[]): The compiler has intrinsic support for arrays. It emits a call to System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray, which allocates and returns a new array (T[]) containing the sliced elements.
  2. It is string: The compiler has intrinsic support for strings. It emits a call to string.Substring, which allocates and returns a new string.
  3. It has an accessible Range indexer: The compiler invokes the indexer passing a System.Range parameter. The captured type matches the return type of the indexer.
  4. It has an accessible Slice(int, int) method: The compiler invokes this method, passing the start index and length. The captured type matches the return type of the Slice method. This is utilized by types like Span<T> and ReadOnlySpan<T> to return a new span without heap allocations.
If the static type supports basic list patterns but does not meet any of these sliceable criteria (e.g., the IList<T> interface), the slice capture or sub-pattern application will fail to compile.
Tired of Poor C# Skills? Fix That With Deep Grasping!Learn More