Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

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.
int[] sequence = { 10, 20, 30, 40, 50 };

// Matches any sequence starting with 10 and ending with 50
bool isMatch = sequence is [10, .., 50]; 

// Matches any sequence starting with 10, 20
bool isPrefixMatch = sequence is [10, 20, ..]; 

2. Capturing Elements

When combined with a var declaration or a specific type, the slice pattern captures the matched elements into a new variable.
int[] sequence = { 10, 20, 30, 40, 50 };

// Captures the elements between the first and last into 'middle'
if (sequence is [10, .. var middle, 50])
{
    // 'middle' is evaluated as int[] containing { 20, 30, 40 }
}

3. Applying Sub-patterns

The slice pattern can be followed by another pattern to evaluate the matched sub-sequence without necessarily capturing it.
int[] sequence = { 10, 20, 30, 40, 50 };

// Applies a property pattern to ensure exactly 3 elements are in the slice
bool hasThreeMiddleElements = sequence is [10, .. { Length: 3 }, 50];

// Applies a list pattern to validate the exact contents of the slice
bool matchesExactMiddle = sequence is [10, .. [20, 30, 40], 50];

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.
int[] shortSequence = { 10, 50 };

// Evaluates to true. 'middle' is captured as an empty int[] { }
bool isZeroMatch = shortSequence is [10, .. var middle, 50];

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.
string text = "A123Z";

// 'stringSlice' is strictly typed as string.
// The compiler emits a call to string.Substring (allocates).
if (text is ['A', .. var stringSlice, 'Z']) 
{
    // stringSlice contains "123"
}

// 'spanSlice' is strictly typed as ReadOnlySpan<char>.
// The compiler emits a call to ReadOnlySpan<T>.Slice(int, int) (no allocation).
if (text.AsSpan() is ['A', .. var spanSlice, 'Z']) 
{
    // spanSlice contains "123"
}

IList<int> list = new int[] { 1, 2, 3, 4 };

// COMPILER ERROR: IList<int> supports list patterns (has Count and this[int]),
// but it is not sliceable (lacks a Range indexer or Slice method).
// The runtime type (int[]) is not evaluated for the capture.
if (list is [1, .. var listSlice, 4]) 
{
}

// This compiles successfully because no slice capture/sub-pattern is used.
if (list is [1, 2, 3, 4])
{
}
Master C# with Deep Grasping Methodology!Learn More