Span-First C#: Designing Around Span<T>
9 hours ago
- Span<T> is a ref struct that provides a type-safe, bounds-checked view over contiguous memory without owning or copying the memory.
- It can wrap arrays, strings, stackalloc blocks, and unmanaged memory via MemoryMarshal.
- Span<T> is stack-only, preventing heap allocation and ensuring safety from GC relocation.
- Four related types (Span<T>, ReadOnlySpan<T>, Memory<T>, ReadOnlyMemory<T>) cover mutable/read-only and stack/heap axes.
- Memory<T> is the heap-storable counterpart for async scenarios, while Span<T> is for synchronous use.
- ref struct restriction by Span<T> enforces lifetime management at compile time, disallowing field storage, boxing, async await, and closures.
- C# 13 allows ref struct as generic type parameter constraint, but does not lift field, closure, or async restrictions.
- Slicing produces a new Span<T> over a subrange without copying, using index and range syntax.
- stackalloc with Span<T> provides stack-allocated buffers with no GC pressure, but is limited to the current stack frame and usually under 1-2 KB.
- Span-first parsing avoids allocations by walking indices over the original buffer and only materializing strings or numbers at final consumption.
- SearchValues<T> (NET 8+) optimizes repeated IndexOfAny calls for tokenizers and delimiter scanning.
- Span-first API design uses ReadOnlySpan<T> as the canonical overload, with array/string overloads as thin convenience wrappers.
- MemoryMarshal allows reinterpreting spans across type boundaries, e.g., casting byte buffers to struct spans without copying.
- Common failure mode: using Span<T> with async methods; fix by using Memory<T> at async layer and Span<T> at synchronous leaf.
- Span-first parsing shows significant performance and memory benefits over string-based parsing, with zero allocations and lower latency.
- Safety caveats include compiler catching direct escapes of stackalloc spans but not all indirect ones, and slice bounds checks being cheap but not free.
- Dictionary keyed by strings cannot use ReadOnlySpan<char> directly without ToString(), but NET 9 offers GetAlternateLookup for span-keyed lookup.
- Span<T> lacks IEnumerable<T> implementation, so LINQ methods are unavailable and must be hand-rolled.