Array și Span în C# (.NET)

Colecții generice .NET Core 2.1+ / C# 7.2+ pentru Span<T>

Lucrează cu blocuri contigue de memorie și felii (slices) de înaltă performanță fără alocări suplimentare pe heap.

Sintaxă C#
int[] arr = [1, 2, 3, 4]; Span<int> felie = arr.AsSpan(1, 2);

Ce face Array și Span<T>

Tablourile clasice (T[]) din C# stochează un număr fix de elemente într-o zonă contiguă de memorie pe heap, oferind cea mai mică amprentă de memorie și acces direct prin index (arr[i], cu proprietatea arr.Length). Totuși, până la apariția lui Span<T>, prelucrarea unei sub-secțiuni dintr-un tablou sau dintr-un string necesita copierea elementelor într-un tablou nou.

Structura System.Span<T> (și varianta sa imutabilă ReadOnlySpan<T>, folosită intens pentru șiruri de caractere prin text.AsSpan()) reprezintă o „fereastră” sigură (ref struct alocată exclusiv pe stivă) peste orice zonă contiguă de memorie: o porțiune dintr-un tablou T[], un string sau un buffer alocat direct pe stivă cu stackalloc. Tăierea unei felii cu span[start..end] se face în timp constant O(1) cu zero alocări pe heap, doar prin ajustarea pointerului intern și a lungimii.

Parametri, metode și opțiuni

Parametru / Metodă Descriere
T[] arr = [a, b, c] Declară și inițializează un tablou de dimensiune fixă; lungimea se citește prin proprietatea arr.Length.
arr.AsSpan() / text.AsSpan() Creează un Span<T> (pentru tablouri) sau un ReadOnlySpan<char> (pentru șiruri) fără copiere de memorie.
span.Slice(int start, int length) / span[a..b] Extrage o sub-fereastră (felie) din Span; modificările făcute în Span<T> se reflectă direct în tabloul original.
span.Fill(T val) / span.Clear() Umple rapid toate pozițiile din felia curentă cu valoarea val sau le resetează la valoarea implicită (0 / null).
stackalloc T[n] Alocă un buffer mic direct pe stiva firului de execuție (fără Garbage Collector), returnând un Span<T>.
Array.Sort(arr) / span.Sort() Sortează in-place întregul tablou sau doar porțiunea vizibilă prin Span<T> (.NET 5+).

Exemple

Exemplul 1: Modificarea in-place a unei porțiuni dintr-un tablou prin Span<int>

Deoarece Span<int> pointează direct către memoria tabloului temperaturi, sortarea sau modificarea feliei modifică direct elementele din mijlocul tabloului original.

int[] temperaturi = [15, 28, 22, 19, 31, 12];

// Luăm o fereastră peste cele 4 elemente din mijloc (index 1..5):
Span<int> mijloc = temperaturi.AsSpan(1, 4);
mijloc.Sort();
mijloc[0] = 20; // modifică direct temperaturi[1]

Console.WriteLine("Tablou complet după modificarea feliei: " + string.Join(", ", temperaturi));

Ieșire în consolă

Tablou complet după modificarea feliei: 15, 20, 22, 28, 31, 12

Exemplul 2: Parsarea numerelor dintr-un șir fără alocări cu ReadOnlySpan<char>

În loc să apelăm Substring (care alocă un nou string pe heap), extragem un ReadOnlySpan<char> și îl pasăm direct metodei int.Parse.

string linieLog = "2026-10-23|STATUS:200|TIMP_MS:48";

ReadOnlySpan<char> span = linieLog.AsSpan();
int an = int.Parse(span[0..4]);
int luna = int.Parse(span[5..7]);
int codStatus = int.Parse(span[18..21]);
int timpMs = int.Parse(span[30..]);

Console.WriteLine($"Data: {an}/{luna:D2} | HTTP {codStatus} | Durată: {timpMs} ms");

Ieșire în consolă

Data: 2026/10 | HTTP 200 | Durată: 48 ms

Greșeli frecvente

  • Încercarea de a stoca un Span<T> ca proprietate într-o clasă obișnuită sau de a-l folosi peste un apel await într-o metodă async — deoarece Span<T> este un ref struct ce poate referi memorie de pe stivă, compilatorul interzice mutarea lui pe heap. În metodele async folosește în schimb Memory<T> sau ReadOnlyMemory<T>.
  • Folosirea excesivă a lui stackalloc pentru buffere foarte mari (de ordinul megabiților), ceea ce poate provoca StackOverflowException; păstrează stackalloc pentru buffere mici (sub 1–4 KB).

Funcții și concepte înrudite

  • List<T>: Stochează o listă generică redimensionabilă dinamic, cu acces rapid după index și metode de adăugare, ștergere și căutare.
  • Replace, Trim și Substring: Curăță spațiile de la capete, înlocuiește fragmente de text și extrage sub-șiruri prin Substring sau operatorul de interval (..).
  • String.Split / String.Join: Desparte un șir de caractere într-un tablou de sub-șiruri după delimitatori și recompune o colecție într-un singur șir.