Span<T>, Memory<T> and low-allocation code
Work with slices of arrays, strings and buffers without copying or allocating: Span, ReadOnlySpan, stackalloc, ArrayPool and Memory.
Span<T> lets you work with part of existing memory without copying it and without allocating anything on the heap.
Span<T> basics
int[] data = { 10, 20, 30, 40, 50, 60 }; Span<int> middle = data.AsSpan(2, 3); // view of 30, 40, 50: no copymiddle[0] = 99; // writes into data!Console.WriteLine(data[2]); Span<int> tail = middle.Slice(1); // slicing a slice: still no copyforeach (int v in tail) Console.Write(v + " ");99 40 50
A span is a reference to memory plus a length. It can point into an array, a string, a stack buffer or native memory. ReadOnlySpan<T>is the read-only version; strings give youReadOnlySpan<char>.Spans are ref structs: they live only on the stack. You can’t store them in fields of classes, put them in lists, or use them acrossawait.
Parsing without allocations
// "B12,IPE300,6.5" → mark, profile, length without Split (which allocates)static double ParseLength(ReadOnlySpan<char> line){ int first = line.IndexOf(','); ReadOnlySpan<char> rest = line[(first + 1)..]; int second = rest.IndexOf(','); ReadOnlySpan<char> lengthText = rest[(second + 1)..]; return double.Parse(lengthText, CultureInfo.InvariantCulture); // parses straight from the span} Console.WriteLine(ParseLength("B12,IPE300,6.5"));6.5
stackalloc
Span<double> temp = stackalloc double[16]; // on the stack: free and instantfor (int i = 0; i < temp.Length; i++) temp[i] = i * 0.5;// use only for SMALL sizes (stack is ~1 MB): a few hundred elements at mostArrayPool: borrow big buffers
using System.Buffers; byte[] buffer = ArrayPool<byte>.Shared.Rent(64 * 1024); // may be bigger than askedtry{ int read = await stream.ReadAsync(buffer.AsMemory(0, 64 * 1024)); Process(buffer.AsSpan(0, read));}finally{ ArrayPool<byte>.Shared.Return(buffer); // give it back for reuse}Memory<T>: when you need to store it or await
Memory<T> is like a span that can live on the heap: store it in a field, keep it across await, put it in a collection. When you need to do the actual work, get a span from it with .Span.
| Type | Lives | Use |
|---|---|---|
Span<T> / ReadOnlySpan<T> | ||
Memory<T> / ReadOnlyMemory<T> | ||
T[] |
static int SumCsv(ReadOnlySpan<char> line) that sums comma-separated integers like "12,5,30,8" without using Split or Substring (use IndexOf and slicing).
Show the solution
static int SumCsv(ReadOnlySpan<char> line){ int sum = 0; while (!line.IsEmpty) { int comma = line.IndexOf(','); ReadOnlySpan<char> part = comma < 0 ? line : line[..comma]; sum += int.Parse(part); line = comma < 0 ? ReadOnlySpan<char>.Empty : line[(comma + 1)..]; } return sum;} Console.WriteLine(SumCsv("12,5,30,8"));55
Remember
Span<T>is a no-copy, no-allocation view over arrays, strings or stack memory.Slice with .Sliceor ranges; writes go to the original memory.Spans are stack-only; use Memory<T>for fields and async code.Use stackalloc(small) andArrayPool(large) to avoid temporary arrays.