C# ReadOnlySpan
last modified April 27, 2025
This tutorial explains how to use the ReadOnlySpan struct in C# to handle memory efficiently. ReadOnlySpan provides a safe way to work with memory slices without allocations.
The ReadOnlySpan struct represents a contiguous region of memory and
is a key feature in .NET that enables efficient handling of data without
creating unnecessary copies. By providing a lightweight, immutable view of
arrays, strings, or raw memory, ReadOnlySpan
optimizes performance
and reduces memory overhead. Its design ensures that any modifications are
prohibited, making it an excellent tool for scenarios where data integrity and
immutability are critical. Additionally, ReadOnlySpan
is
stack-allocated, contributing to lower garbage collection pressure and better
overall application performance.
ReadOnlySpan
is particularly well-suited for tasks like parsing
structured data, processing substrings, or working with data buffers, especially
in high-performance applications. Its type safety ensures that developers can
confidently work with strongly-typed data, while its built-in bounds checking
prevents out-of-range access, reducing the likelihood of runtime errors. These
characteristics make ReadOnlySpan
a powerful abstraction for
handling contiguous data in memory-intensive operations, where both reliability
and efficiency are essential.
Basic ReadOnlySpan Example
This example shows how to create a ReadOnlySpan from an array and access its elements without copying the data.
int[] numbers = { 1, 2, 3, 4, 5 }; ReadOnlySpan<int> span = numbers.AsReadOnlySpan(); Console.WriteLine($"First: {span[0]}"); Console.WriteLine($"Length: {span.Length}"); Console.WriteLine($"Slice (2-4): {string.Join(", ", span.Slice(2, 2).ToArray())}");
The program creates a ReadOnlySpan
from an integer array and
demonstrates accessing elements and slicing. AsReadOnlySpan
creates
a span without copying the array. The example accesses the first element using
indexing, retrieves the span's length, and creates a slice of two elements
starting at index 2.
Since ReadOnlySpan
is immutable, it ensures the underlying data
cannot be modified, providing safety while maintaining performance by avoiding
memory allocations.
Slicing Strings with ReadOnlySpan
ReadOnlySpan is useful for efficient string manipulation. This example parses a string without creating substrings.
string text = "Hello, World!"; ReadOnlySpan<char> span = text.AsSpan(); ReadOnlySpan<char> hello = span.Slice(0, 5); ReadOnlySpan<char> world = span.Slice(7, 5); Console.WriteLine($"First word: {hello.ToString()}"); Console.WriteLine($"Second word: {world.ToString()}");
The example creates a ReadOnlySpan
from a string and extracts
substrings using slicing. AsSpan
creates a span over the string's
characters without copying. The program slices the span to extract "Hello" and
"World" by specifying start indices and lengths.
Unlike traditional substring methods, slicing with ReadOnlySpan
avoids allocating new strings, improving performance. The ToString
method converts the spans back to strings for display, but the slicing itself is
allocation-free.
Parsing Numbers with ReadOnlySpan
ReadOnlySpan
can parse data efficiently. This example parses
integers from a comma-separated string.
using System; string data = "42,100,7"; ReadOnlySpan<char> span = data.AsSpan(); List<int> numbers = []; while (!span.IsEmpty) { int commaIndex = span.IndexOf(','); ReadOnlySpan<char> numberSpan = commaIndex == -1 ? span : span.Slice(0, commaIndex); numbers.Add(int.Parse(numberSpan)); span = commaIndex == -1 ? [] : span.Slice(commaIndex + 1); } Console.WriteLine($"Parsed numbers: {string.Join(", ", numbers)}");
The program parses integers from a string using ReadOnlySpan
to
avoid allocations. It creates a span from the input string and iteratively finds
commas to split the data. For each segment, IndexOf
locates the
next comma, and Slice
extracts the number portion.
int.Parse
converts the span to an integer, which is added to a
list.
The span is updated to skip the comma and continue parsing. This approach is efficient because it avoids creating intermediate strings, relying instead on ReadOnlySpan's ability to work directly with the string's memory.
Working with Memory Buffers
ReadOnlySpan can handle raw memory buffers. This example processes a byte buffer as a span.
byte[] buffer = { 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }; ReadOnlySpanspan = buffer; int firstInt = BitConverter.ToInt32(span.Slice(0, 4)); int secondInt = BitConverter.ToInt32(span.Slice(4, 4)); Console.WriteLine($"First int: {firstInt}"); Console.WriteLine($"Second int: {secondInt}");
The example treats a byte array as a ReadOnlySpan to read integers from a
buffer. The array contains two 32-bit integers in little-endian format.
Slice
extracts 4-byte segments, and
BitConverter.ToInt32
converts each segment to an integer. Using
ReadOnlySpan
avoids copying the buffer and provides a safe,
bounds-checked way to access the data. This technique is useful for processing
binary data, such as network packets or file formats, where direct memory access
is needed without allocations.
Using ReadOnlySpan with Stack Allocation
ReadOnlySpan can work with stack-allocated memory for maximum efficiency. This example uses stackalloc.
using System; Span<int> temp = stackalloc int[3]; temp[0] = 10; temp[1] = 20; temp[2] = 30; ReadOnlySpan<int> span = temp; Console.WriteLine($"Sum: {span[0] + span[1] + span[2]}"); Console.WriteLine($"Slice (1-2): {string.Join(", ", span.Slice(1, 2).ToArray())}");
The program uses stackalloc
to allocate a temporary array on the
stack, which is converted to a ReadOnlySpan. The Span<int>
is
filled with values, then cast to a ReadOnlySpan for safe, read-only access.
The example calculates the sum of the elements and creates a slice. Stack
allocation avoids heap allocations, making this approach extremely efficient
for small, short-lived buffers.
ReadOnlySpan
ensures the data is accessed safely with bounds
checking, and its immutability prevents accidental modifications to the
underlying memory.
Comparing Strings with ReadOnlySpan
ReadOnlySpan enables efficient string comparisons. This example compares portions of strings.
using System; string text1 = "Hello, World!"; string text2 = "Hello, Universe!"; ReadOnlySpan<char> span1 = text1.AsSpan(0, 5); ReadOnlySpan<char> span2 = text2.AsSpan(0, 5); bool areEqual = span1.SequenceEqual(span2); Console.WriteLine($"First 5 chars equal: {areEqual}");
The example compares the first five characters of two strings using
ReadOnlySpan. AsSpan
creates spans over the relevant portions of
the strings, and SequenceEqual
checks if they are identical.
This method is more efficient than creating substrings or using traditional string comparison methods because it operates directly on the string's memory without allocations. This technique is particularly useful in performance-critical scenarios, such as parsing or validating large datasets, where minimizing memory usage and processing time is crucial.
ReadOnlySpan with ReadOnlyMemory
ReadOnlySpan
can work with ReadOnlyMemory
for flexible
memory management. This example demonstrates conversion.
using System; int[] numbers = { 1, 2, 3, 4, 5 }; ReadOnlyMemory<int> memory = numbers.AsMemory(); ReadOnlySpan<int> span = memory.Span; Console.WriteLine($"First: {span[0]}"); Console.WriteLine($"Last: {span[^1]}");
The program converts an array to a ReadOnlyMemory
, then extracts a
ReadOnlySpan from it. AsMemory
creates a memory object, and the
Span
property provides a span for direct access. The example
accesses the first and last elements using indexing. ReadOnlyMemory
is useful for passing memory regions between methods or async operations, while
ReadOnlySpan
provides a lightweight, stack-based view for immediate
processing. This combination allows flexible and efficient memory management,
particularly in scenarios involving asynchronous or layered data processing.
Source
ReadOnlySpan Struct Documentation
This tutorial covered using ReadOnlySpan in C# for efficient memory handling, including arrays, strings, buffers, and stack-allocated memory.
Author
List all C# tutorials.