Iterators lader dig producere en sekvens af værdier dient efter dient ved hjælp af yield-nøgleordet — uden at bygge hele samlingen op i hukommelsen. En metode, der bruger yield return, bliver en iterator, der genererer værdier efter behov, ideelt til store eller uendelige sekvenser og brugerdefineret iteration.
yield return — lazy sekvensgeneration
// a method with `yield return` is an ITERATOR — produces values one at a time
public IEnumerable<int> GetNumbers(int n)
{
for (int i = 0; i < n; i++)
{
yield return i; // produce a value, PAUSE here, resume on the next request
}
}
foreach (var num in GetNumbers(1000000)) // generates lazily — minimal memory
{
Console.WriteLine(num);
if (num > 5) break; // only generated 7 values — the rest were never computed
}
yield return producerer en værdi og pauser metoden, genoptager hvor den slap af ved næste iteration (bevarer tilstand). Værdier genereres efter behov — intet beregnes før det anmodes, og et break stopper genereringen tidligt.
Hukommelsesfordelen (lazy vs eager)
// ❌ eager — builds the WHOLE list in memory first
public List<int> GetAll(int n)
{
var list = new List<int>();
for (int i = 0; i < n; i++) list.Add(i); // all n values in memory
return list;
}
// ✅ iterator — generates lazily, constant memory regardless of n
public IEnumerable<int> GetAll(int n)
{
for (int i = 0; i < n; i++) yield return i;
}
Uendelige sekvenser (umuligt med en liste)
public IEnumerable<int> Fibonacci()
{
int a = 0, b = 1;
while (true) // infinite — but lazy, so it's fine
{
yield return a;
(a, b) = (b, a + b);
}
}
var first10 = Fibonacci().Take(10); // take only what you need
yield og LINQ
LINQ's deferred execution is built on iterators — Where/Select use yield internally
to process elements lazily, one at a time, as the result is enumerated.
Hvorfor det betyder noget
Iterators og yield-nøgleordet er vigtige C#-funktioner til hukommelseseffektiv, lazy sekvensgeneration, og at forstå dem er værdifuldt til at behandle store datasæt og til at forstå, hvordan LINQ fungerer.
Hovedfordelen er at producere sekvenser lazy, en værdi ad gangen, efter behov — i stedet for at bygge en hel samling op i hukommelsen — hvilket er væsentligt for store datasæt (hvor materialisering af alt ville opbruge hukommelsen), streaming-/behandlingsscenarier og endda uendelige sekvenser (som at generere Fibonacci-tal, umuligt med en liste, men trivielt med en lazy iterator, kun tager det du har brug for).
yield return-mekanismen (producering af en værdi, pause, genoptagelse med bevaret tilstand) gør det rent og naturligt at skrive sådanne lazy-generatorer.
At forstå iterators belyser også, hvordan LINQs deferred execution fungerer — LINQ-operatorer bruger iterators internt til at behandle elementer dient efter dient, når resultatet opregnes, hvilket forklarer LINQs lazy-adfærd.
At kende iterators (lazy-generation med yield, hukommelsesfordelene frem for eager collection-building, uendelige sekvenser og forbindelsen til LINQs deferred execution) er værdifuldt til at skrive hukommelseseffektiv data-behandlingskode og til at forstå C#'s lazy evaluation-model.
Omend ikke nødvendigt for ethvert scenarie, er iterators vigtige ved behandling af store mængder data effektivt eller ved opbygning af brugerdefinerede lazy-sekvenser, og at forstå dem forøger din forståelse af LINQ og lazy-evaluering — hvilket gør dem til nyttig, praktisk-relevant viden til effektiv C# og et emne, der afspejler forståelse af sprogets deferred-execution-egenskaber.
