Spans & Memory in C#
Work with stack-allocated slices using Span<T>, ReadOnlySpan<T>, Memory<T>, and MemoryMarshal for zero-allocation string parsing and buffer manipulation.
Why Span<T>?
Every time you call `string.Substring()`, `array.ToArray()`, or LINQ on an array, C# allocates a new object on the heap. For hot paths — parsers, network protocols, game loops — these small allocations add up and pressure the garbage collector. `Span<T>` is a stack-only struct that points to a contiguous region of memory without owning or copying it. It lets you slice, read, and write data with zero heap allocation.
1. The Allocation Problem
Demonstrate how common string/array operations cause hidden heap allocations, then show how Span eliminates them.
Span<T> & ReadOnlySpan<T>
`Span<T>` is a writable view into a contiguous block of memory — an array segment, a stack-allocated buffer, or unmanaged memory. `ReadOnlySpan<T>` is the immutable variant — strings expose themselves as `ReadOnlySpan<char>`. Both support slicing with ranges, searching, and comparison.
1. Creating Spans
Span can be created from arrays, array segments, stackalloc, and strings. Each points into the original memory.
2. Span Methods — Search, Compare, Copy
Span provides `IndexOf`, `Contains`, `StartsWith`, `SequenceEqual`, `CopyTo`, `Fill`, and `Reverse` — all without allocating.
3. Zero-Allocation String Parsing
Parse structured text — CSV, log lines, dates — using Span slicing and `int.Parse(ReadOnlySpan<char>)` without any string allocations.
Memory<T> & ReadOnlyMemory<T>
`Span<T>` is a ref struct — it can only live on the stack. You cannot store it in a class field, put it in a `List`, or use it across an `await`. `Memory<T>` solves this: it is a regular struct (not ref struct) that wraps the same zero-copy slice concept but can be stored anywhere. Call `.Span` on a `Memory<T>` to get a `Span<T>` for synchronous processing.
1. Memory<T> Basics
`Memory<T>` can be stored in class fields and passed across `await`. Convert to `Span<T>` with `.Span` when you need to read or write the data synchronously.
2. ArrayPool<T> — Renting Reusable Buffers
Instead of allocating a new array for temporary work, rent one from `ArrayPool<T>`, use it, then return it. Combined with Span, this gives allocation-free temporary buffers.