If you have ever worked with strings, or arrays in C#, you have probably seen code that copies data more times than needed.
Extra copying increases memory usage and hurts performance. Modern applications handle large data streams, buffers, network packets, and processing pipelines, so controlling allocations matters.
Spans, gives you a safe view over existing memory without allocating new memory.
What Is Span<T>?
Span, is a value type that enables the representation of contiguous regions of arbitrary memory.
It does not allocate memory.
It is stack-only
It works with arrays, strings, stack memory, and unmanaged memory.
It supports slicing without copying data.
A Simple Example
Without Span<T> (Traditional Approach)
If you wanted elements 3, 4, 5 from the array before Span existed, you would usually write:
int[] numbers = { 1, 2, 3, 4, 5, 6 };
// We want { 3, 4, 5 }
int[] slice = new int[3];
for (int i = 0; i < 3; i++)
slice[i] = numbers[i + 2];
foreach (var x in slice)
Console.WriteLine(x);
What happens here?
A new array is created.
The values {3, 4, 5} are copied into it.
That means extra memory + extra CPU work.
This is called allocation, and allocations in .NET always go to the managed heap, which:
Takes space, thus increases memory usage.
Causes garbage collection to run more often, thus affects performance.
With Span<T> (Zero Allocation Approach)
int[] numbers = { 1, 2, 3, 4, 5, 6 };
// Create a span over the array
Span<int> span = numbers;
// Slice the span (no copying)
Span<int> slice = span.Slice(2, 3);
foreach (var x in slice)
Console.WriteLine(x);
What happens now?
Slive does not create a new array.
It points directly to part of the existing array.
No integers are copied.
No new memory is allocated.
But be careful, any changes made to Span slice it will be also made in created array since we are pointing to it in the first place.
Visually
slice is simply a window over the existing data.
Working With Strings Using ReadOnlySpan<char>
Strings are immutable. If you split or substring, you usually create new strings.
Example
string text = "HelloWorld";
string part = text.Substring(5); // Allocates a new string
Using spans
ReadOnlySpan<char> span = text;
ReadOnlySpan<char> partSpan = span.Slice(5);
No allocation. Very helpful when parsing large text.
Real Use Case: Parsing Numbers
string input = "12345";
ReadOnlySpan<char> span = input;
int result = int.Parse(span); // Works directly with ReadOnlySpan
Console.WriteLine(result); // 12345
No intermediate string allocations.
Important Constraint: Span<T> Is Stack Only
You cannot do this:
class Test
{
private Span<int> field; // Not allowed
}
Because Span may refer to stack memory which would be unsafe if stored beyond the current scope. This is a deliberate safety guarantee.
If you need something similar that can live on the heap, use Memory<T> or ReadOnlyMemory<T>.