Iterátorok

Akkor válasszon iterátort, ha olyan sorozatot szeretne visszaadni, amelynek elemeit a hívók egyenként használhatják fel anélkül, hogy a teljes eredményt egy gyűjteménybe építenék. Ez a megközelítés jól használható generált értékek esetén, az egyéni vezérlési folyamattal rendelkező szűrőkhöz, valamint azokhoz a sorozatokhoz, amelyek olvasását a hívók idő előtt abbahagyhatják. A befejezett gyűjteményhez képest az iterátor megváltoztatja, hogy mikor történik a munka, nem csak azt, ahogyan megírja.

Tip

Ez a cikk ismerteti a szinkron iterátorokat a metódusokat, hurkokat és gyűjteményeket ismerő fejlesztőknek. A teljes yield szintaxist és korlátozásokat lásd az yieldutasításreferenciát.

Az iterátormetódus egyenként egymás után állítja elő az értékek sorozatát. Ez a cikk azokat az iterátormetódusokat ismerteti, amelyek IEnumerable<T> értéket adnak vissza, és amelyek értékeit a hívók általában egy foreach utasítással használják fel.

Elemek létrehozása a(z) yield return elemmel

A sorozat következő elemének megadására szolgál yield return . Az alábbi iterátor egy érzékelő elfogadható hőmérséklet-értékeit választja ki. Ebben a forgatókönyvben -1 az érzékelő adatainak végét jelöli.

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;
        }
    }
}

SelectTemperatures a(z) IEnumerable<int> értéket adja vissza, amely int értékek sorozatát jelöli. Mindegyik yield return reading egy értéket biztosít a foreach hurok számára. Miután a hívó kezeli ezt az értéket, az iterátor a következő bemenettel folytatódik.

Használja az általános IEnumerable<T> formát a legtöbb modern C#-kódhoz. A típusargumentum ( például int) jelzi a hívóknak, hogy a sorozat melyik elemtípust tartalmazza.

A sorozat befejezése a következővel: yield break

Egy iterátor lezárásához használja a yield break elemet, mielőtt a vezérlés eléri a metódus végét. A példában a sentinel érték -1 azt jelenti, hogy nincs több mérési érték:

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

Az iterátor természetesen véget is ér, amikor a végrehajtás eléri a törzse végét. Használja a yield break elemet, ha egy feltételnek azonnal le kell állítania az elemgyártást.

Iterátor bejárása foreach

Az iterátor ugyanolyan IEnumerable<T> szerkezetet tesz elérhetővé, mint sok .NET-gyűjtemény. A hívók anélkül használhatják foreach , hogy tudnák, hogyan állítja elő a sorozat az elemeit:

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

A meghívó kód minden elemet lekér, ahogy a hurok előrehalad. Az iterátor megtartja a kérések közötti pozícióját, és az előző yield returnután is folytatódik .

A késleltetett elemek előállításának megértése

Az iterátor metódus meghívása nem futtatja azonnal a törzsét. A metódus akkor kezd elemeket létrehozni, amikor a hívó elkezdi az enumerálást. Ezt a viselkedést halasztott végrehajtásnak nevezzük.

A példa minden Checking ... üzenet előtt kiírja a Sequence created. elemet. Az iterátor csak akkor kezdi el ellenőrizni az olvasatokat, amikor a foreach hurok az első elemet kéri.

A halasztott végrehajtás gyakorlati hatással bír:

  • A hívó elkezdheti a feldolgozást, mielőtt az iterátor az összes elemet visszaadná.
  • Az iterátor korán yield breakleállhat.
  • Az a hívó, amely soha nem járja be a sorozatot, nem futtatja az iterátor törzsét.
  • Minden új enumerálás újra futtatja az iterátort.

Tedd egyértelművé a késleltetett végrehajtást a metódus céljában és dokumentációjában. Az olvasók önmagában a IEnumerable<T> alapján nem tudnak következtetni a végrehajtás időzítésére, mert az iterátorok és a kollekciók is megvalósíthatják ezt az interfészt. Az elemgyártás legyen mentes a nem kapcsolódó mellékhatásoktól, mert a hívók később vagy többször is bejárhatják a sorozatot. A példa diagnosztikai üzenetei ezt az időzítést szemléltetik.

Iterátor vagy gyűjtemény kiválasztása

Iterátort ad vissza, ha az értékek természetesen egyenként érkeznek, vagy ha a hívónak nincs szüksége azonnal a teljes eredményre. Teljes gyűjteményt ad vissza, ha a hívóknak stabil pillanatképre, indexelt hozzáférésre vagy ismételt bejárásra van szükségük az elemek újraszámítása nélkül.

Mindkét módszer ugyanazokat az elemeket tudja visszaadni. Az iterátor az elemek előállítását a hívó felügyelete alatt hagyja; a gyűjteményt összeállító metódus még a visszatérése előtt befejezi ezt a munkát.

Akkor használjon szokásos hurkot, ha minden elemnél műveletet szeretne végrehajtani anélkül, hogy sorozatot adna vissza. Standard szűrő vagy átalakítás esetén előnyben részesítse a LINQ-műveletet, például Where vagy Select; írjon iterátort, ha az egyéni elemelőállítási logika egyértelműbb utasítássorozatként.

Az iterátor metódusok együtt használhatók a LINQ-val, mert mindkettő a(z) IEnumerable<T>-t használja. Egy hívó például hozzáadhat egy másik szűrőt:

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

Az iterátor és a LINQ művelet halasztva marad, amíg a kód fel nem sorolja a(z) comfortableReadings elemet.

Az aszinkron szekvenciákról az aszinkron streamek létrehozása és felhasználása című témakörben olvashat. Az egyéni számbavételi mintákhoz és az összes yield korlátozáshoz használja a nyelvi referenciát.

Lásd még