Lesson 24: Unsafe Code, Span<T>, and High-Performance C#
Write zero-allocation code with Span<T>, Memory<T>, ArrayPool, and raw pointers for maximum performance.
What You'll Learn
- • Span<T> and ReadOnlySpan<T> for zero-copy slicing
- • Memory<T> and ArrayPool<T> for heap-safe buffers
- • Unsafe code blocks, pointers, and the fixed statement
- • When (and when not) to use these features
🧠 Real-World Analogy
Think of Span<T> as a window into a bookshelf — you can see and rearrange books through the window, but you never move the bookshelf itself. unsafe code is like removing the glass entirely: more access, but also more risk.
Span<T> — Zero-Copy Slicing
Span<T> is a stack-only type that provides a safe, bounds-checked view over contiguous memory — arrays, strings, or even stack-allocated buffers. It lets you slice and manipulate data without allocating new arrays, which is critical in hot paths.
Span<T> & ReadOnlySpan<T>
Slice arrays and strings without allocations using Span.
using System;
class Program
{
static void Main()
{
// Span<T> — a stack-only view over contiguous memory
int[] numbers = { 10, 20, 30, 40, 50, 60, 70, 80 };
// Create a span over the entire array (zero-copy!)
Span<int> fullSpan = numbers.AsSpan();
Console.WriteLine($"Full span length: {fullSpan.Length}");
// Slice without allocating a new array
Span<int> middle = fullSpan.Slice(2, 4); // {30, 40, 50, 60}
C
...Memory<T> & ArrayPool
Unlike Span<T>, Memory<T> can be stored on the heap (in fields, async methods, etc.). Combined with ArrayPool<T>, you can eliminate most large array allocations entirely.
Memory<T> & ArrayPool
Use heap-safe Memory<T> and ArrayPool to eliminate GC pressure.
using System;
using System.Buffers;
class Program
{
static void Main()
{
// Memory<T> — like Span<T> but can live on the heap
int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Memory<int> memory = data.AsMemory();
// Slice without copying
Memory<int> slice = memory.Slice(3, 4);
Console.WriteLine($"Slice: [{string.Join(", ", slice.ToArray())}]");
// Convert to Span when you need to work with the data
Span<int
...Unsafe Code & Pointers
C# allows raw pointer manipulation in unsafe blocks. You must compile with /unsafe flag. Use fixed to pin managed objects so the GC won't move them while you're using pointers. This is primarily used for interop with native libraries or extreme performance optimization.
Unsafe Pointers & fixed Statement
Use raw pointers, pointer arithmetic, and stackalloc in unsafe blocks.
using System;
using System.Runtime.InteropServices;
class Program
{
// Mark the method as unsafe to use pointers
static unsafe void Main()
{
// Pointer to a stack variable
int value = 42;
int* ptr = &value;
Console.WriteLine($"Value via pointer: {*ptr}");
// Pointer arithmetic with arrays
int[] array = { 10, 20, 30, 40, 50 };
fixed (int* p = array) // Pin the array so GC won't move it
{
for (int i =
...Pro Tip
Prefer Span<T> over unsafe pointers in almost all cases. Span gives you bounds checking and safety while matching pointer performance. Reserve unsafe for P/Invoke or truly specialized scenarios.
Common Mistakes
- • Returning
Span<T>from a method that usesstackalloc— the memory is gone when the method returns - • Forgetting to return rented arrays to ArrayPool — causes memory leaks
- • Using
unsafewhenSpan<T>would suffice — adds complexity without benefit
Lesson Complete!
You've mastered Span, Memory, ArrayPool, and unsafe pointers. Next, learn to manage resources correctly with IDisposable.
Sign up for free to track which lessons you've completed and get learning reminders.