Skip to content

C# ReadOnlySpan — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about C# ReadOnlySpan. We cover key concepts, practical examples, and best practices.

Your method receives an array or string and only reads it — no modification needed. You pass the array directly, but the caller does not know you will not modify it. ReadOnlySpan<T> provides a read-only, allocation-free view of contiguous memory that guarantees immutability.

Wrong

public int CountSpaces(string text)
{
    int count = 0;
    foreach (char c in text)
    {
        if (c == ' ') count++;
    }
    return count;
}

Output: Works. But the method signature says nothing about immutability — it just happens not to modify text.

public int CountSpaces(ReadOnlySpan<char> text)
{
    int count = 0;
    foreach (char c in text)
    {
        if (c == ' ') count++;
    }
    return count;
}

Usage:

CountSpaces("hello world");        // From string
CountSpaces(buffer.AsSpan());      // From array
CountSpaces(stackalloc char[10]);  // From stack memory

Output: Same result. The method accepts any read-only contiguous memory — strings, arrays, stack-allocation.

ReadOnlySpan<T> is the read-only counterpart of Span<T>. It is a stack-only struct (ref struct) with zero overhead.

Prevention

  • Use ReadOnlySpan<T> for parameters that only read the data.
  • Use ReadOnlySpan<T> over string for parsing methods to avoid substrings.
  • Use ReadOnlySpan<char> for high-performance string processing.
  • Use Span<T> only when you need to modify the data.
  • Use Memory<T> / ReadOnlyMemory<T> when the data must be stored on the heap (e.g., async methods).
  • Understand that ReadOnlySpan<T> cannot be used in async methods, lambdas, or as a field.

Common Mistakes with readonly span

  1. Forgetting deriving (Show, Eq) on custom data types needed for debugging
  2. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
  3. Using head and tail instead of pattern matching, causing runtime errors on empty lists

These mistakes appear frequently in real-world CSHARP code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

What is the difference between ReadOnlySpan and Span?

ReadOnlySpan<T> is a read-only view — you cannot modify elements through it. Span<T> allows modification. Both are stack-only ref structs with the same performance characteristics. Use ReadOnlySpan<T> to communicate immutability in your API.

Can I create a ReadOnlySpan from a string?

Yes. "hello".AsSpan() creates a ReadOnlySpan<char> over the string's internal buffer — zero allocation. This is the primary use case for high-performance string processing.

Why can't I use ReadOnlySpan in async methods?

ReadOnlySpan<T> is a ref struct — it can only live on the stack. Async methods suspend execution, and the stack frame is not preserved. Use ReadOnlyMemory<T> for async scenarios: it is a heap-safe wrapper that can be used across awaits.

ReadOnlySpan<T> is used throughout Durga Antivirus Pro for zero-allocation file scanning. For more C# performance tips, visit DodaTech.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro