The Ultimate Guide to List and Span Patterns in C# 14

C# 14 continues to evolve pattern matching into one of the language’s most expressive and high-performance features. Among its most powerful additions are list and span patterns, which allow developers to match, destructure, and validate sequences of data in a declarative, safe, and efficient way. Whether working with arrays, List<T>, or Span<T>, these patterns enable readable, maintainable, and high-performance code in enterprise systems, AI pipelines, IoT applications, and more.

This article provides a comprehensive guide to list and span patterns, including syntax, behavior, advanced techniques, performance considerations, and real-world applications.


1. Understanding List and Span Patterns

List and span patterns extend C#’s pattern matching system to indexable sequences. They allow developers to:

  • Match elements by position: [first, second, third]
  • Capture the rest of a sequence: [first, ..rest]
  • Combine with relational, logical, and type patterns: [>0, <10, ..]
  • Apply to arrays, List<T>, spans, and memory slices

Key distinction:

  • List patterns operate on IList<T> or arrays.
  • Span patterns operate on Span<T> and ReadOnlySpan<T> for allocation-free, high-performance code.

2. Basic Syntax

int[] numbers = { 1, 2, 3, 4, 5 };

// Match exact sequence
if (numbers is [1, 2, 3, 4, 5])
    Console.WriteLine("Exact match!");

// Capture remaining elements
if (numbers is [1, 2, .. var rest])
    Console.WriteLine($"Remaining: {string.Join(", ", rest)}");
  • ..rest captures all remaining elements after the explicitly matched ones.
  • Can be combined with relational or type patterns.

3. Span Patterns

Span<int> span = stackalloc int[] { 10, 20, 30 };

if (span is [10, > 15, ..])
    Console.WriteLine("Matched relational pattern in span!");

Highlights:

  • Spans provide stack-allocated, allocation-free memory access.
  • Patterns over spans are JIT-optimised for high-performance workloads.
  • Ideal for real-time systems, streaming data, and low-latency pipelines.

4. Advanced Pattern Features

4.1 Combining Relational and Logical Patterns

int[] data = { 5, 10, 15 };
if (data is [> 0, < 20, .. var rest])
    Console.WriteLine($"Matched first two with conditions. Remaining: {string.Join(", ", rest)}");
  • Logical combinations can be applied to individual elements.
  • Reduces nested conditional logic, improving readability.

4.2 Nested List Patterns

int[][] matrix = { new[] {1, 2}, new[] {3, 4} };

if (matrix is [[1, 2], [> 2, _]])
    Console.WriteLine("Nested matrix pattern matched!");
  • Supports multi-dimensional arrays and jagged arrays.
  • Patterns can recurse into sub-arrays and nested sequences.

4.3 Span Slice Patterns

Span<int> numbers = stackalloc int[] { 1, 2, 3, 4, 5 };

if (numbers is [1, .. var mid, 5])
    Console.WriteLine($"Middle elements: {string.Join(", ", mid.ToArray())}");
  • Capture head, middle, and tail sections efficiently.
  • Works with spans, avoiding heap allocations.

5. Integration with Other Pattern Types

  • Relational: [> 0, < 10, ..]
  • Logical: [>0 and <10, ..]
  • Type: [int first, string second, ..]
  • Property patterns in sequences: people is [{ Age: >=18 }, ..]

Example:

var people = new[] { new Person { Age = 20 }, new Person { Age = 15 } };

if (people is [{ Age: >=18 }, .. var minors])
    Console.WriteLine($"Number of minors: {minors.Length}");

6. Real-World Applications

DomainExample
Data Validation[>0, >0, ..var rest] ensures first elements are positive
Sequence Parsing[command, parameter, ..args] splits CLI input or CSV rows
Matrix/Grid Checks[[>0, _], [_ , >0]] validates 2×2 grids or game boards
AI/ML Pipelines[>0, <1, ..] normalises input sequences for neural networks
IoT / Sensor Streams[temperature, pressure, ..] checks first readings and captures remainder

7. Performance Considerations

  • Zero heap allocations for spans.
  • Compiler converts patterns into efficient IL instructions.
  • JIT optimisations reduce branching overhead.
  • Capturing slices (..) is O(1) for spans and avoids array copies.
  • Ideal for high-throughput, real-time, and memory-sensitive applications.

8. Best Practices

  1. Use .. sparingly to maintain readability.
  2. Combine relational and logical patterns for explicit validation.
  3. Prefer spans for performance-critical code.
  4. Document nested patterns to avoid complexity.
  5. Use patterns for parsing, validation, and preprocessing in a declarative way.

9. Architectural Impact

  • Declarative and maintainable sequence validation improves readability across large systems.
  • Memory-efficient for cloud, IoT, AI/ML, and game engines.
  • Encourages functional, pattern-first programming, reducing imperative boilerplate.
  • Safe and expressive: minimises off-by-one and index errors.
  • Works seamlessly with relational, logical, type, and property patterns, making code modular and composable.

10. Comparison: Arrays vs Lists vs Spans

FeatureArrayList<T>Span<T>
Supports pattern matching
Stack allocation
Heap allocation
PerformanceGoodModerateExcellent
Mutable lengthFixedVariableFixed (view)

Use spans for high-performance and allocation-free scenarios, arrays for static sequences, and List<T> when dynamic sizing is required.


11. Summary

List and Span Patterns in C# 14 provide:

  • Direct element matching and remainder capture [first, ..rest]
  • Support for arrays, lists, and spans
  • Integration with relational, logical, type, and property patterns
  • Nested and multi-dimensional sequence matching
  • Declarative, readable, maintainable, and high-performance code

Impact: Developers can now handle sequence validation, parsing, and preprocessing safely and efficiently, suitable for enterprise, AI/ML, IoT, gaming, and cloud systems. Enhanced list and span patterns mark a paradigm shift in how C# handles collections, making code expressive, safe, and highly performant.