Biorąc pod uwagę ten kod:
IEnumerable<object> FilteredList()
{
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
yield return item;
}
}
Dlaczego nie miałbym tego po prostu zakodować w ten sposób ?:
IEnumerable<object> FilteredList()
{
var list = new List<object>();
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
list.Add(item);
}
return list;
}
W pewnym sensie rozumiem, co yield
robi słowo kluczowe. Mówi kompilatorowi, aby zbudował pewien rodzaj rzeczy (iterator). Ale po co go używać? Poza tym, że jest nieco mniej kodu, co to robi dla mnie?
FullList.Where(IsItemInPartialList)
:)Odpowiedzi:
Użycie
yield
powoduje, że kolekcja jest leniwa.Powiedzmy, że potrzebujesz tylko pierwszych pięciu elementów. Na swój sposób muszę przeglądać całą listę, aby uzyskać pierwsze pięć pozycji. W programie przeglądam
yield
tylko pierwsze pięć elementów.źródło
FullList.Where(IsItemInPartialList)
będzie równie leniwe. Tylko, że wymaga znacznie mniej niestandardowego kodu wygenerowanego przez kompilator. I mniej czasu programisty na pisanie i konserwację (Oczywiście, że właśnie w tym przykładzie)yield return
) tam, gdzie było to możliwe.Zaletą bloków iteratorów jest to, że działają leniwie. Możesz więc napisać taką metodę filtrowania:
To pozwoli ci filtrować strumień tak długo, jak chcesz, nigdy nie buforując więcej niż jednego elementu na raz. Jeśli na przykład potrzebujesz tylko pierwszej wartości ze zwróconej sekwencji, dlaczego miałbyś chcieć skopiować wszystko do nowej listy?
Jako inny przykład, możesz łatwo utworzyć nieskończony strumień przy użyciu bloków iteratorów. Na przykład, oto sekwencja liczb losowych:
Jak zapisałbyś nieskończoną sekwencję na liście?
Moja seria blogów Edulinq zawiera przykładową implementację LINQ to Objects, która w dużym stopniu wykorzystuje bloki iteratorów. LINQ jest zasadniczo leniwy tam, gdzie może być - a umieszczanie rzeczy na liście po prostu nie działa w ten sposób.
źródło
RandomSequence
czy nie. Dla mnie IEnumerable oznacza - po pierwsze i przede wszystkim - że mogę iterować z foreach, ale to oczywiście doprowadziłoby do nieskończonej pętli. Uważam to za dość niebezpieczne nadużycie koncepcji IEnumerable, ale YMMV.IEnumerable<BigInteger>
ciąg reprezentujący ciąg Fibonacciego. Możeszforeach
z nim korzystać, ale nic nieIEnumerable<T>
gwarantuje, że będzie on ograniczony.Z kodem „listy” musisz przetworzyć pełną listę, zanim będziesz mógł przekazać ją do następnego kroku. Wersja „yield” przenosi przetworzony element natychmiast do następnego kroku. Jeśli ten „następny krok” zawiera „.Take (10)”, to wersja „yield” przetworzy tylko pierwsze 10 elementów i zapomni o reszcie. Kod „listy” przetworzyłby wszystko.
Oznacza to, że największą różnicę widać, gdy trzeba dużo przetworzyć i / lub mieć długie listy elementów do przetworzenia.
źródło
Możesz użyć,
yield
aby zwrócić elementy, których nie ma na liście. Oto mała próbka, która może powtarzać się w nieskończoność po liście, dopóki nie zostanie anulowana.To pisze
...itp. do konsoli do czasu anulowania.
źródło
Gdy powyższy kod jest używany do pętli przez FilteredList () i zakładając, że item.Name == "James" będzie spełniony na 2. pozycji na liście, metoda wykorzystująca
yield
zwróci dwa razy. To jest leniwe zachowanie.Gdzie jako metoda używająca list doda wszystkie n obiektów do listy i przekaże pełną listę do metody wywołującej.
To jest dokładnie przypadek użycia, w którym można wyróżnić różnicę między IEnumerable i IList.
źródło
Najlepszym przykładem użycia programu w świecie rzeczywistym, jaki widziałem,
yield
byłoby obliczenie ciągu Fibonacciego.Rozważ następujący kod:
To zwróci:
Jest to przyjemne, ponieważ pozwala szybko i łatwo obliczyć nieskończoną serię, dając możliwość korzystania z rozszerzeń Linq i przeszukiwania tylko tego, czego potrzebujesz.
źródło
Czasami jest to przydatne, czasami nie. Jeśli cały zestaw danych musi zostać zbadany i zwrócony, nie będzie żadnych korzyści z używania wydajności, ponieważ wszystko, co zrobił, to wprowadzenie narzutu.
Kiedy wydajność naprawdę świeci, jest zwracana tylko część zestawu. Myślę, że najlepszym przykładem jest sortowanie. Załóżmy, że masz listę obiektów zawierającą datę i kwotę w dolarach z tego roku i chciałbyś zobaczyć pierwszą garść (5) rekordów z tego roku.
Aby to osiągnąć, listę należy posortować rosnąco według daty, a następnie wziąć pierwszych 5. Jeśli zrobiono to bez plonów, całość lista musiałaby zostać posortowana, aż do upewnienia się, że ostatnie dwie daty są w porządku.
Jednak z wydajnością po ustaleniu pierwszych 5 pozycji sortowanie zatrzymuje się i wyniki są dostępne. Może to zaoszczędzić dużo czasu.
źródło
Instrukcja zwrotu zysku umożliwia zwrócenie tylko jednej pozycji naraz. Zbierasz wszystkie elementy na liście i ponownie zwracasz tę listę, co jest narzutem pamięci.
źródło