Artykuł

sty 23 2013
0

Szybkie zwracanie kolekcji z yield

Programując często tworzymy wiele podobnych konstrukcji i rozwiązań. Niektóre z nich da się ładnie przemodelować i umieścić w bibliotece. Inne można uprościć wykorzystując odpowiednie konstrukcje języka np. ostatnio omawianą klasę generyczną Tuple.

Krótszy kod, to oczywiście mniejszy rozmiar programu, a w niektórych przypadkach również zwiększona czytelność, dlatego w dzisiejszym wpisie chciałbym zaprezentować słowo kluczowe yield, które powinno przypaść do gustu wszystkim operującym na kolekcjach w C#. Czyli tak na dobrą sprawę, wszystkim programistom tego języka;)

Idea stosowania słowa kluczowego yield w C#

Nie wiem jak Wy, ale osobiście często operuje na różnorakich kolekcjach, które w jakiś sposób muszę przefiltrować, posortować, czy też po prostu stworzyć na podstawie określonych danych. Nieodłączonym elementem kolekcji, są pętle - przeważnie for i foreach. Za ich pomocą możemy iterować po kolejnych elementach zbioru.

Wyobraźmy sobie teraz prosty przykład metody, która dla zbioru x elementów, zwróci kolekcję tylko tych, które podzielne są przez określony dzielnik. Standardowo zrobiliśmy by to mniej więcej tak:

public IEnumerable<int> GetInts(int nNumberOfElements, int nDivider)
{
    Collection<int> oCollection = new Collection<int>();
    for (int nNumber = 0; nNumber < nNumberOfElements; ++nNumber)
    {
        if (0 == (nNumber % nDivider))
        {
            oCollection.Add(nNumber);
        }
    }
    return oCollection;
}

Wewnątrz metody tworzymy nową kolekcję, którą później wypełniamy w pętli for i zwracamy za pomocą słowa kluczowego return. Ta stosunkowo prosta operacja, zabrała nam aż 11 linijek. Okazuje się, że z pomocą naszego tytułowego yield, można ten kod skrócić i uprościć:

public IEnumerable<int> GetInts(int nNumberOfElements, int nDivider)
{
    for (int nNumber = 0; nNumber < nNumberOfElements; ++nNumber)
    {
        if (0 == (nNumber % nDivider))
        {
            yield return nNumber;
        }
    }
}

Nowy kod zajmuje tylko 9 linijek i dodatkowo uprościliśmy wyrażenie wewnątrz instrukcji if. Słówko yield powoduje, że kolekcja bazująca na interfejsie IEnumerable, automatycznie się inicjalizuje i wypełnia danymi w transparentny dla nas sposób.

Słowo yield przed wyrażeniem return, gwarantuje automatyczny powrót do wcześniej rozpoczętej pętli.

Poznajcie yield break

yield można również wywołać ze słowem kluczowym break. Wyobraźmy sobie, że chcemy zmodyfikować nasz przykład w taki sposób by co prawda zwracać liczby podzielne przez wartość wskazaną w parametrze, ale jednocześnie chcemy przerwać w działanie w przypadku gdy jakaś pasująca liczba podzielna jest również przez 11. Spójrzmy na zmodyfikowany przykład:

public IEnumerable<int> GetInts(int nNumberOfElements, int nDivider)
{
    for (int nNumber = 0; nNumber < nNumberOfElements; ++nNumber)
    {
        if (0 == (nNumber % nDivider))
        {
            // W wyniku dzielenia przez 0 nigdy nie otrzymamy reszty 
            if((nNumber > 0) && (0 == (nNumber % 11)))
            {
                yield break;
            }
            yield return nNumber;
        }
    }
}

Konstrukcja ta jest o tyle ciekawa, ponieważ umożliwia zwrócenie całej kolekcji pozyskanej przed wystąpieniem wyrażenia yield break. Czyli jest przykładowo wywołamy metodę w następujący sposób:

GetInts(40, 3);

To uzyskamy liczby ze zbioru:

3,6,9,...,27,30,33

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus