C# 2.0: yield

Publikoval Michal Kočí dňa 17.9.2004 o 23:17 v kategórii .Net

Určite každý pozná klúčové slovo foreach, ktoré zabezpečí zbehnutie kódu istej slučky pre každý prvok pola, kolekcie a pod.

Napríklad nasledujúci kód zabezpečí, že do konzolového okna sa vypíše každý prvok pola poleRetazcov:

string[] poleRetazcov = "c#;je;skvely".Split(';');
foreach(string retazec in poleRetazcov)
{
    Console.WriteLine(retazec);
}
Do konzolového okna sa tak pod seba vypíšu slová "c#", "je" a "skvelý". Foreach toto dokáže zabezpečiť preto, lebo trieda Array implementuje rozhranie (interface) IEnumerable, konkrétne jedinú metódu GetEnumerator tohto rozhrania. To znamená, že ak ste doteraz chceli, aby sa aj na Váš objekt dal použiť foreach, museli ste aj vy implementovať toto rozhranie. Metóda GetEnumerator musí vrátiť rozhranie IEnumerator. Objekt, ktorý implementuje toto rozhranie musí implementovať metódy Reset a MoveNext a vlastnosť Current. Je jasné, že je to docela pracné (nie však zložité).

C# túto činnosť zjednodušuje prínosom klúčového slova yield. Klúčové slovo yield sa môže použiť v dvoch konštrukciach:

  • yield return - vráti jeden prvok. Pri ďaľsej iterácii sa bude pokračovať na tom mieste, kde bola táto konštrukcie použitá.
  • yield break - ukončí proces iterácie, čiže dáva najavo, že už nie je k dispozícii žiaden ďaľší prvok

Ako veľmi jednoduchý príklad môžem uviesť triedu, ktorej konštruktor prijíma jeden reťazec, od ktorého očakáva, že obsahuje slová oddelené bodkočiarkou. Táto trieda si interne tento reťazec rozparsuje a jednotlivé prvky si uloží do poľa. Ak na triedu použijeme foreach, očakávame, že postupne prejdeme všetky obsiahnuté slová.

public class StringArrayExample
{
    private string[] stringArray;
    public StringArrayExample(string delimitedString)
    {
        stringArray = delimitedString.Split(';');
    }
    public IEnumerator<string> GetEnumerator()
    {
        foreach (string oneString in stringArray)
        {
            yield return oneString;
    }
}
}
Vidíme, že trieda okrem konštruktora obsahuje iba jednu metódu a to metódu GetEnumerator, ktorá vracia typ IEnumerator. Čiže žiadna veda, celé je to jednoduché a vystačíme si s jedinou metódou. Navyše, použitím Generics nám kompilátor sám ustráži, že v cykle foreach nad objektom tejto triedy môžeme použiť iba typ String. Akýkoľvek iný typ nám spôsobí chybu pri kompilácii. Použitie je jednoduché, opäť si necháme jednotlivé prvky vypísať do konzolového okna:
StringArrayExample saeExample = new StringArrayExample("c#;je;skvely");
foreach (string oneString in saeExample)
{
    Console.WriteLine(oneString);
}

yield musíme použiť v tzv. iterator bloku, ktorým môže byť s istými obmedzeniami metóda, operátor alebo accessor. Metóda môže prijímať aj parametre avšak tieto nesmú byť ani ref ani out. Ukážkou môže byť trieda, ktorej v konštruktore predáme názov súboru a metóda slúžiaca ako iterator blok bude prijímať celočíselný parameter indikujúci koľko riadkov zo súboru chceme vrátiť. Menej ich vrátime v prípade, ak týmto počtom riadkov súbor nedisponuje:

public class FileLineReader
{
    private string fileName;
    public FileLineReader(string fileName)
    {
        this.fileName = fileName;
    }
    public IEnumerable<string> ReadLines(int count)
    {
        if (File.Exists(fileName))
        {
            int iRead = 0;
            FileStream fsRead = new FileStream(fileName, FileMode.Open);
            StreamReader srRead = new StreamReader(fsRead);

            while ((!srRead.EndOfStream) && (iRead < count))
            {
                iRead++;
                yield return srRead.ReadLine();
            }

            srRead.Close();
        }
    }
}
Trochu menej šetrné na pamäť, avšak šetrnejšie k zdrojom systému by asi bolo súbor si v konštruktore načítať do pamäti. Takisto by bolo vhodné použiť try/catch/finally blok. Isté obmedzenia platia aj na tento blok, viac info v nápovede.

Teda konštatujem, že je to ďaľšia užitočná črta dvojkovej verzie C# a to ich ešte zopár je, ale o tých snáď nabudúce.

Mohlo by ťa tiež zaujímať

Páčil sa ti príspevok?

Zdieľaj príspevok alebo si ho odlož na neskôr

Sleduj ma

Ak nechceš premeškať príspevky ako je tento, sleduj ma na Twitteri, alebo ak máš RSS čítačku, môžeš sledovať môj RSS kanál.