Yield – Leniwe kolekcje
Yield
Instrukcja Yield wykorzystywana jest do tworzenia kolekcji, gdzie kolejne elementy kolekcji są tworzone w momencie ich wykorzystania. Umożliwia to zaoszczędzenie pamięci oraz przyśpieszenie działania aplikacji. Jeśli chcemy wygenerować obiekty naszej listy możemy wykorzystać instrukcję yield return:
1 2 3 4 5 6 7 8 |
private static IEnumerable<int> GetValues() { for(int i=0; i<10;i++) { Console.WriteLine("{1} generated at {0}", DateTime.Now.Ticks, i); yield return i; } } |
Metoda generująca nasze elementy zostanie wykonana dopiero w momencie odczytu elementu poprzez enumerator. Odczytem jest każda operacja, która pobiera wartość, i tak dla :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Program { static void Main(string[] args) { foreach (int item in GetValues()) { Console.WriteLine("{0} uset ",item); } Console.ReadLine(); } private static IEnumerable<int> GetValues() { for(int i=0; i<10;i++) { Console.WriteLine("{0} generated", i); yield return i; } } } |
w wyniku otrzymamy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
0 generated 0 uset 1 generated 1 uset 2 generated 2 uset 3 generated 3 uset 4 generated 4 uset 5 generated 5 uset 6 generated 6 uset 7 generated 7 uset 8 generated 8 uset 9 generated 9 uset |
a gdy tylko będziemy chcieli odwrócić kolejność elementów przy pomocy reverse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Program { static void Main(string[] args) { foreach (int item in GetValues().Reverse()) { Console.WriteLine("{0} uset ", item); } Console.ReadLine(); } private static IEnumerable<int> GetValues() { for (int i = 0; i < 10; i++) { Console.WriteLine("{0} generated", i); yield return i; } } } |
otrzymamy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
0 generated 1 generated 2 generated 3 generated 4 generated 5 generated 6 generated 7 generated 8 generated 9 generated 9 uset 8 uset 7 uset 6 uset 5 uset 4 uset 3 uset 2 uset 1 uset 0 uset |
Jeśli chcemy zakończyć kolekcję w dowolnym momencie możemy wykorzystać yield break.
1 2 3 4 5 |
private static IEnumerable<int> GetEmpty() { yield break; } |
Zastosowanie
Leniwe kolekcje mogą być wykorzystane wszędzie tam, gdzie wykorzystujemy normalny IEnumerable, jednakże swoją potęgę pokazują, kiedy mamy do czynienia z dużymi zbiorami danych. Jeśli zbiór danych znajduje się na dysku w postaci pliku możemy odczytywać jego zawartość sekwencyjnie i zwracać dane w mniejszych partiach. Przy odczycie i zwróceniu od razu całej kolekcji w postaci listy, pełen zbiór danych musiałby zostać odczytany i przechowany w pamięci aplikacji.
Innym przypadkiem użycia może być trudna generacja danych dla kolekcji. Jeśli generacja takich danych jest trudna nie potrzebujemy od razu znać pełnej kolekcji, niekiedy potrzebujemy tylko kilka pierwszych wartości. Przy zastosowaniu listy musielibyśmy obliczyć od razu wszystkie elementy, a z wykorzystaniem yield elementy są obliczane wraz z postępem przetwarzania naszej kolekcji.