Ce face IEnumerable<T> și yield return
IEnumerable<T> este interfața de bază pentru tot ce se poate parcurge: tablouri, liste, rezultate LINQ. O metodă care returnează IEnumerable<T> și conține yield return devine un iterator: compilatorul generează în spate o mașină de stări.
Execuția este amânată (lazy): codul metodei nu rulează la apel, ci doar când cineva parcurge rezultatul, și se oprește după fiecare yield return până se cere elementul următor. yield break încheie secvența. Așa poți genera secvențe foarte mari sau infinite fără să le ții în memorie.
Fiecare parcurgere rulează din nou codul iteratorului. Dacă ai nevoie de rezultat de mai multe ori sau de numărul de elemente, materializează-l o dată cu ToList() sau ToArray().
Exemple
Exemplul 1: Execuție amânată
Mesajele arată că elementele sunt produse unul câte unul, intercalat cu consumul lor.
var numere = Genereaza(3);
Console.WriteLine("Înainte de foreach");
foreach (int n in numere)
Console.WriteLine($"Primit {n}");
static IEnumerable<int> Genereaza(int cate)
{
for (int i = 1; i <= cate; i++)
{
Console.WriteLine($"Produc {i}");
yield return i * 10;
}
}
Rezultat
Înainte de foreach
Produc 1
Primit 10
Produc 2
Primit 20
Produc 3
Primit 30
Exemplul 2: Secvență infinită oprită cu Take și yield break
Console.WriteLine(string.Join(", ", Fibonacci().Take(8)));
Console.WriteLine(string.Join(", ", PanaLa(["Ana", "Ion", "STOP", "Dan"])));
static IEnumerable<long> Fibonacci()
{
long a = 0, b = 1;
while (true)
{
yield return a;
(a, b) = (b, a + b);
}
}
static IEnumerable<string> PanaLa(IEnumerable<string> nume)
{
foreach (string n in nume)
{
if (n == "STOP")
yield break;
yield return n;
}
}
Rezultat
0, 1, 1, 2, 3, 5, 8, 13
Ana, Ion
Greșeli frecvente
- Validarea argumentelor într-un iterator se face abia la prima parcurgere, nu la apel. Pune verificarea într-o metodă obișnuită care apoi returnează iteratorul dintr-o funcție locală.
- Parcurgerea de mai multe ori a unui
IEnumerablecostisitor (citire din fișier, interogare) repetă munca. FoloseșteToList()o dată. yield returnnu poate apărea într-un bloctrycare arecatch, nici în metode cu parametriref/out.
Comenzi înrudite
foreach: Parcurge pe rând fiecare element al unui tablou, al unei liste sau al oricărui IEnumerable, fără indice.Skip / Take (paginare): Take ia primele n elemente, Skip le sare; împreună fac paginare. TakeWhile/SkipWhile se opresc la prima condiție falsă.Constrângeri generice (where T : ...): Clauza where limitează tipurile acceptate de un generic (class, struct, new(), o interfață sau clasă de bază) ca să le poți folosi membrii.