Iteratoren

Kies een iterator wanneer u een reeks wilt retourneren waarvan de aanroepers één voor één kunnen gebruiken, zonder eerst het volledige resultaat in een verzameling te bouwen. Deze benadering past bij gegenereerde waarden, filters met aangepaste controlestroom en reeksen die bellers mogelijk niet meer vroeg kunnen lezen. Vergeleken met een voltooide verzameling verandert een iterator wanneer het werk plaatsvindt, niet alleen hoe u het schrijft.

Tip

In dit artikel leert u synchrone iterators voor ontwikkelaars die methoden, lussen en verzamelingen kennen. Zie de yield instructiereferentie voor volledige yield syntaxis en beperkingen.

Een iteratormethode produceert een reeks waarden één voor één. Dit artikel is gericht op iteratormethoden die retourneren IEnumerable<T>, waarvan de aanroepers van waarden meestal een foreach instructie gebruiken.

Elementen produceren met yield return

Gebruik yield return dit om het volgende element in de reeks op te geven. De volgende iterator selecteert acceptabele temperatuurmetingen van een sensor. In dit scenario -1 markeert u het einde van de sensorgegevens.

int[] readings = [21, 27, 24, -1, 19];

IEnumerable<int> acceptedReadings =
    SelectTemperatures(readings, maximum: 25);

Console.WriteLine("Sequence created.");

foreach (int temperature in acceptedReadings)
{
    Console.WriteLine($"Accepted: {temperature}°C");
}

static IEnumerable<int> SelectTemperatures(
    IEnumerable<int> readings,
    int maximum)
{
    foreach (int reading in readings)
    {
        Console.WriteLine($"Checking {reading}°C");

        if (reading == -1)
        {
            yield break;
        }

        if (reading <= maximum)
        {
            yield return reading;
        }
    }
}

SelectTemperaturesretourneert , dat een reeks int waarden vertegenwoordigtIEnumerable<int>. Elk yield return reading biedt één waarde aan de foreach lus. Nadat de aanroeper die waarde heeft verwerkt, gaat de iterator verder met de volgende invoer.

Gebruik het algemene IEnumerable<T> formulier voor de meeste moderne C#-code. Het typeargument, zoals int, vertelt bellers welk element de reeks bevat.

De reeks beëindigen met yield break

Gebruik yield break dit om een iterator te beëindigen voordat het besturingselement het einde van de methode bereikt. In het voorbeeld betekent de sentinel-waarde -1 dat er geen leeswaarden meer beschikbaar zijn:

if (reading == -1)
{
    yield break;
}

De iterator eindigt ook op natuurlijke wijze wanneer de uitvoering het einde van de hoofdtekst bereikt. Gebruik yield break wanneer een voorwaarde de productie van elementen onmiddellijk moet stoppen.

Een iterator gebruiken met foreach

Een iterator toont dezelfde IEnumerable<T> vorm als veel .NET verzamelingen. Bellers kunnen zonder foreach te weten hoe de reeks de elementen produceert:

foreach (int temperature in SelectTemperatures(readings, 25))
{
    Console.WriteLine($"Accepted: {temperature}°C");
}

De aanroeper vraagt elk element aan wanneer de lus verder gaat. De iterator houdt de positie tussen aanvragen en gaat verder na de vorige yield return.

Meer informatie over de productie van uitgestelde elementen

Als u een iterator-methode aanroept, wordt de hoofdtekst niet onmiddellijk uitgevoerd. De methode begint elementen te produceren wanneer de aanroeper begint met inventariseren. Dit gedrag wordt uitgestelde uitvoering genoemd.

In het voorbeeld worden alle berichten afgedrukt Sequence created.Checking ... . De iterator begint alleen met het controleren van de leeswaarden wanneer de lus om het foreach eerste element vraagt.

Uitgestelde uitvoering heeft praktische effecten:

  • De aanroeper kan beginnen met verwerken voordat de iterator elk element produceert.
  • De iterator kan vroeg stoppen met yield break.
  • Een aanroeper die de reeks nooit opsommen, voert de hoofdtekst van de iterator niet uit.
  • Elke nieuwe inventarisatie voert de iterator opnieuw uit.

Maak de uitgestelde uitvoering duidelijk in het doel en de documentatie van de methode. Lezers kunnen de uitvoeringstijd IEnumerable<T> niet alleen afleiden, omdat zowel iterators als verzamelingen die interface kunnen blootstellen. Houd elementproductie vrij van niet-gerelateerde bijwerkingen, omdat bellers de reeks later of meer dan één keer kunnen inventariseren. De diagnostische berichten in het voorbeeld illustreren die timing.

Een iterator of verzameling kiezen

Retourneer een iterator wanneer waarden natuurlijk één voor één binnenkomen of wanneer de beller het volledige resultaat niet onmiddellijk nodig heeft. Retourneert een voltooide verzameling wanneer bellers een stabiele momentopname, geïndexeerde toegang of herhaalde opsomming nodig hebben zonder de elementen opnieuw te berekenen.

Beide benaderingen kunnen dezelfde elementen retourneren. De iterator verlaat de elementproductie onder het besturingselement van de beller; een methode waarmee een verzameling wordt gebouwd, wordt voltooid voordat deze wordt geretourneerd.

Gebruik een gewone lus als u een actie wilt uitvoeren voor elk item zonder een reeks te retourneren. Voor een standaardfilter of transformatie geeft u de voorkeur aan een LINQ-bewerking zoals Where of Select; schrijft u een iterator wanneer aangepaste logica voor elementproductie duidelijker is als een reeks instructies.

Iterator-methoden opstellen met LINQ omdat beide worden gebruikt IEnumerable<T>. Een beller kan bijvoorbeeld een ander filter toevoegen:

var comfortableReadings = SelectTemperatures(readings, 25)
    .Where(static temperature => temperature >= 18);

De iterator- en LINQ-bewerking blijven uitgesteld totdat de code wordt opgesomd comfortableReadings.

Zie Asynchrone stromen genereren en gebruiken voor asynchrone reeksen. Gebruik de taalverwijzing voor aangepaste opsommingspatronen en alle yield beperkingen.

Zie ook