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 apelawaitîntr-o metodăasync— deoareceSpan<T>este unref structce poate referi memorie de pe stivă, compilatorul interzice mutarea lui pe heap. În metodeleasyncfolosește în schimbMemory<T>sauReadOnlyMemory<T>. - Folosirea excesivă a lui
stackallocpentru buffere foarte mari (de ordinul megabiților), ceea ce poate provocaStackOverflowException; păstreazăstackallocpentru 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.