Tipuri ref struct și Câmpuri ref în C#: Garanții de Memorie pe Stivă și By-Reference Safety în C# (.NET)

Ghid complet pentru tipurile ref struct și câmpurile ref introduse în C# 11 și C# 12: asigurarea alocării exclusive pe stivă, regulile stricte de siguranță a duratei de viață și crearea de structuri ultra-rapide cu zero alocare.

Sintaxă C#
ref struct BufferReader { ref int _cursor; ReadOnlySpan<byte> _data; }

În dezvoltarea componentelor critice de rețea, parsere JSON sau protocoale financiare, alocarea obiectelor pe Heap aduce o penalizare dublă: latența apelării alocatorului de memorie și costul ulterior de curățare al Garbage Collector-ului.

Modificatorul ref struct forțează compilatorul să garanteze contractual că o structură nu poate părăsi niciodată stiva de execuție a firului curent. Începând cu C# 11 și C# 12, structurile ref struct pot declara și câmpuri ref (ref fields), permițând crearea de containere de nivel înalt peste memorie nativă sau felii de date.

Definirea unui Parser Rapid cu ref struct și ref fields
// ref struct trăiește exclusiv pe stivă:
public ref struct ParserPachete
{
    private ReadOnlySpan<byte> _buffer;
    private ref int _pozitieCurenta; // Câmp ref către un întreg extern

    public ParserPachete(ReadOnlySpan<byte> buffer, ref int cursor)
    {
        _buffer = buffer;
        _pozitieCurenta = ref cursor;
    }

    public byte CitesteUrmatorulOctet()
    {
        byte valoare = _buffer[_pozitieCurenta];
        _pozitieCurenta++;
        return valoare;
    }
}

public class Program
{
    public static void Main()
    {
        byte[] pachet = [0xAA, 0xBB, 0xCC];
        int index = 0;

        // Parser-ul rulează instantaneu pe stivă:
        var parser = new ParserPachete(pachet, ref index);
        byte b1 = parser.CitesteUrmatorulOctet();

        Console.WriteLine($"Octet citit: 0x{b1:X2}, Index actualizat: {index}");
    }
}

Restricții Impuse de Compilator pentru Siguranța Memoriei

Restricție Compilator Motivație Tehnică
Nu poate fi boxată (nu poate fi convertită la object sau ValueType). Boxing-ul ar forța copierea structurii pe Heap, încălcând garanția de stivă.
Nu poate fi câmp într-o clasă normală. Clasele trăiesc pe Heap; o clasă nu poate deține o referință către o stivă care poate dispărea.
Nu poate fi folosită în metode async. Metodele asincrone generează o mașină de stări pe Heap care poate supraviețui apelului inițial.