*
Previous Async Streams in C# Code Coverage and Static Analysis in C# Next

📏 Span<T> in C#

Span<T> in C#

Span<T> is a value type in C# that represents a contiguous block of memory. It provides a safe, low-overhead way to work with sections of memory from various sources without allocating new memory on the heap. This makes it a powerful tool for writing high-performance, low-allocation code.

What Span<T> is

Think of Span<T> as a "view" or "slice" into a block of memory. The memory it points to can come from:

  • An array or a section of an array.
  • A string (using the immutable ReadOnlySpan<char>).
  • Stack-allocated memory (using the stackalloc keyword).
  • Unmanaged or native memory.

Key characteristics of Span<T>

  • Zero heap allocation: Unlike creating a Substring or Subarray, which makes a new copy on the managed heap, a Span<T> is allocated on the stack. This eliminates overhead and reduces garbage collection pressure.
  • Performance: Because it is a ref struct that holds a direct reference and a length, indexing a Span<T> is as fast as indexing a raw array.
  • Memory safety: The C# compiler enforces strict rules to prevent Span<T> from being used in ways that could lead to memory corruption, such as escaping the stack.
  • Slicing: The Slice() method allows you to easily create a new Span<T> that refers to a subset of the original memory, all without any additional memory allocation.

Example

int[] numbers = { 10, 20, 30, 40, 50 };
Span<int> slice = numbers.AsSpan(1, 3); // Refers to elements [20, 30, 40]

foreach (var num in slice)
{
Console.WriteLine(num);
} 

Version Introduced

Span<T> was introduced in C# 7.2 and supported by the .NET Core 2.1 runtime. A version was also backported for use with older .NET versions via a NuGet package, but the deepest optimizations were included in .NET Core.

Limitations and Alternatives

Due to its stack-only nature, Span<T> comes with a few limitations:

  • It cannot be used as a field in a class.
  • It cannot be used inside async methods, iterator blocks, or across await and yield boundaries.
  • It cannot be boxed or used as a generic type argument.

For asynchronous operations or scenarios where the memory needs to live on the heap, use its companion type Memory<T>. Memory<T> can be stored on the heap and safely passed across async boundaries. When you need to perform high-performance, synchronous work on it, you can access its Span property.

Example Using Memory<T>

Memory<int> memory = new int[] { 1, 2, 3, 4, 5 };
Span<int> span = memory.Span;
span[0] = 99;
Console.WriteLine(memory.Span[0]); // Output: 99

Summary

  • Span<T> is best for high-performance, stack-based, synchronous operations.
  • Use Memory<T> when you need heap allocation or async compatibility.
  • They both improve performance by reducing memory allocations and enabling safe access to memory.

📏 Span<T> in C#

Span<T> is a stack-only, memory-safe type introduced in C# 7.2 that provides a view over contiguous memory. It allows slicing and manipulation of arrays, strings, or unmanaged memory without additional allocations.

đź’ˇ Example: Using Span<T>

  using System;

  class Program {
      static void Main() {
          int[] numbers = { 1, 2, 3, 4, 5, 6 };
          Span<int> span = numbers;

          // Slice the span
          Span<int> middle = span.Slice(2, 3); // {3, 4, 5}

          // Modify the slice
          for (int i = 0; i < middle.Length; i++) {
              middle[i] *= 10;
          }

          // Output original array to show changes
          foreach (var num in numbers) {
              Console.WriteLine(num);
          }
      }
  }
  

âś… Best Practices

  • Use Span<T> for short-lived, high-performance operations.
  • Prefer ReadOnlySpan<T> when you don’t need to modify data.
  • Use stackalloc for temporary buffers on the stack.
  • Avoid returning Span<T> from methods—it's stack-bound.
  • Use Memory<T> for async or heap-based scenarios.

📌 When to Use

  • For performance-critical code that avoids heap allocations.
  • When working with slices of arrays or buffers.
  • In parsing routines (e.g., string manipulation).
  • For interop scenarios with unmanaged memory.

đźš« When Not to Use

  • In async methods—use Memory<T> instead.
  • For long-lived data—Span is stack-bound and short-lived.
  • In class fields—Span cannot be stored in heap objects.

⚠️ Precautions

  • Don’t escape Span outside its stack scope.
  • Be cautious with unsafe code when using Span over unmanaged memory.
  • Ensure bounds safety when slicing or indexing.
  • Use ReadOnlySpan to prevent accidental mutations.

🎯 Advantages

  • Zero allocations: No heap usage for slicing and manipulation.
  • Memory safety: Bounds-checked and type-safe.
  • Performance: Ideal for high-throughput scenarios.
  • Versatility: Works with arrays, strings, stackalloc, and unmanaged memory.
  • Cleaner code: Reduces need for temporary arrays or buffers.

📝 Conclusion

Span<T> is a powerful tool for efficient memory access in C#. It’s best suited for performance-sensitive, short-lived operations where safety and speed matter. Use it wisely to write faster and cleaner code.

Back to Index
Previous Async Streams in C# Code Coverage and Static Analysis in C# Next
*
*