2015-10-12 27 views
7

Mam IEnumerable<T>, który chcę filtrować na podstawie predykatu LINQ. Próbowałem używać Where na IEnumerable jak zwykle, ale tym razem natrafiłem na coś interesującego. Podczas wywoływania Where na IEnumerable, z predykatem, otrzymuję w zamian pustą listę. Wiem, że musi utworzyć listę zawierającą dwa elementy. Jeśli zamiast tego użyję FindAll, z tym samym predykatem, to wtedy wygeneruje poprawny wynik.FindAll Vs Gdzie

Czy ktoś może mi wyjaśnić, dlaczego tak się dzieje? Zawsze uważałem, że Where było leniwą wersją FindAll, która również zwróciła wartość IEnumerable zamiast List. Czy to musi być coś więcej? (Zrobiłem rozeznanie, ale bezskutecznie.)

Kod:

IEnumerable<View> views = currentProject.Views.Where(
        v => v.Entries.Any(e => e.Type == InputType.IMAGE || e.Type == InputType.VIDEO)); 

IEnumerable<View> views = currentProject.Views.FindAll(
        v => v.Entries.Any(e => e.Type == InputType.IMAGE || e.Type == InputType.VIDEO)); 
+11

'List .FindAll' zwraca 'List ' podczas gdy 'Where' zwraca' IEnumerable '. Ale 'Where' używa odroczonego wykonywania, więc dostajesz je tylko wtedy, gdy go zmaterializujesz, f.e. z 'ToList'. –

+5

Dlaczego nie zamieściłeś tego jako odpowiedzi? :) Lepsze wyjaśnienie niż jedyna odpowiedź tutaj, która została zamieszczona później niż komentarz. –

+0

Pomimo tego, że w twoim komentarzu udzielono odpowiedzi na pytanie, została wysłana odpowiedź, która odpowiada na nią dokładniej niż jakakolwiek z odpowiedzi na powiązane pytanie. –

Odpowiedz

2

Mój najlepszy przypuszczenie byłoby, że coś się dzieje między wywołaniem Gdzie, który tworzy wyliczający i miejsce w kodzie gdzie są rzeczywiście używane wyniki (tzn. gdzie faktycznie są wywoływane MoveNext i (get_) Current tego modułu wyliczającego, np. z ToList).

+0

Problem polegał na tym, że nie zadzwoniłem .ToList() podczas używania Where() na IEnumerable, więc inspekcja wykazała pustą listę. –

1

Tak, gdzie jest leniwa wersja findall. FindAll() jest funkcją typu List, nie jest to metoda rozszerzenia LINQ, taka jak Where. Metoda FindAll na liście, która jest metodą instancji, która zwraca nową listę z tym samym typem elementu. FindAll można używać tylko w instancjach List, a metody LINQ działają na każdym typie implementującym IEnumerable.

Główna różnica (poza tym, w czym są realizowane: IEnumerable vs. List) jest taka, że ​​Where implementuje odroczoną realizację, gdzie nie wykonuje wyszukiwania dopóki nie jest potrzebna, (używając go w pętli foreach dla przykład). FindAll jest metodą natychmiastowej realizacji.

Będę odwoływał się do struktury danych nazywanej drzewem wyrażeń, aby zrozumieć odroczone wykonanie, wystarczy tylko zrozumieć, że drzewo wyrażeń jest strukturą danych, taką jak lista lub kolejka. Przechowuje zapytanie LINQ do SQL, a nie wyniki zapytania, ale rzeczywiste elementy samego zapytania.

Aby zrozumieć Where działać musimy zobaczyć, że jeśli napisać kod

var query = from customer in db.Customers 
     where customer.City == "Paris" 
     select customer;    

Query nie wykonuje tutaj natomiast wykonać w pętli foreach

do zrozumienia LINQ and Deferred Execution