IEnumerable și yield return în C# (.NET)

IEnumerable<T> este orice secvență care se poate parcurge cu foreach; yield return produce elementele pe rând, doar când sunt cerute.

Sintaxă C#
IEnumerable<int> Pare(int max) { for (int i = 0; i <= max; i += 2) yield return i; }

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 IEnumerable costisitor (citire din fișier, interogare) repetă munca. Folosește ToList() o dată.
  • yield return nu poate apărea într-un bloc try care are catch, nici în metode cu parametri ref/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.