<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. C#
  3. Advanced C#
All lessons in this course

Lesson 52 of 60 15 minAdvanced

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.

High-performance code (parsers, file importers, geometry kernels, render loops) spends a surprising amount of time creating small temporary arrays and strings. Span<T> lets you work with part of existing memory without copying it and without allocating anything on the heap.

Interactive

A window onto memory

Span<int> slice = data.AsSpan(3, 4); // no copy
slice.Length = 4. slice[0] is data[3]. Making the span cost nothing: no new array was allocated.
Parsing "B12,IPE300,6.5" one million times
line.Split(',')
per line: 1 array + 3 new strings = 4 allocations
1M lines: 4,000,000 objects for the GC
line.AsSpan() + IndexOf(',') + Slice
per line: slices of the original text = 0 allocations
1M lines: 0 objects, parse numbers straight from the span

Span<T> basics

Span.cs
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 + " ");
Output
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 you ReadOnlySpan<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 across await.

Parsing without allocations

Parse.cs
// "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"));
Output
6.5

stackalloc

StackAlloc.cs
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 most

ArrayPool: borrow big buffers

Pool.cs
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.

TypeLivesUse
Span<T> / ReadOnlySpan<T>stack onlyfast synchronous processing of a slice
Memory<T> / ReadOnlyMemory<T>anywhereasync APIs, storing a slice for later
T[]heapowning data long-term
Quick check

After var s = arr.AsSpan(1, 2); s[0] = 5;, what is arr[1]?

Quick check

Why can’t a Span<T> be a field of a class?

Practice

Write 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
Solution.cs
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"));
Output
55

Remember

  • Span<T> is a no-copy, no-allocation view over arrays, strings or stack memory.
  • Slice with .Slice or ranges; writes go to the original memory.
  • Spans are stack-only; use Memory<T> for fields and async code.
  • Use stackalloc (small) and ArrayPool (large) to avoid temporary arrays.
0 of 60 lessons completed