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>andReadOnlySpan<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)}");
..restcaptures 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
| Domain | Example |
|---|---|
| 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
- Use
..sparingly to maintain readability. - Combine relational and logical patterns for explicit validation.
- Prefer spans for performance-critical code.
- Document nested patterns to avoid complexity.
- 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
| Feature | Array | List<T> | Span<T> |
|---|---|---|---|
| Supports pattern matching | ✅ | ✅ | ✅ |
| Stack allocation | ❌ | ❌ | ✅ |
| Heap allocation | ✅ | ✅ | ❌ |
| Performance | Good | Moderate | Excellent |
| Mutable length | Fixed | Variable | Fixed (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.