2015-10-12 77 views
7

Ho un IEnumerable<T> che ho voluto filtrare in base a un predicato LINQ. Ho provato a utilizzare Where sullo IEnumerable come al solito, ma questa volta sono incappato in qualcosa di interessante. Quando si chiama Where su the IEnumerable, con il predicato, ottengo una lista vuota in cambio. So che deve produrre un elenco con due elementi. Se invece utilizzo FindAll, con lo stesso predicato, esso produce il risultato corretto.FindAll Vs Where

Qualcuno può spiegarmi perché questo sta accadendo? Ho sempre pensato che Where fosse una specie di versione lazy di FindAll, che restituiva anche un IEnumerable invece di uno List. Ci deve essere più di questo? (Ho fatto qualche ricerca, ma inutilmente.)

Codice:

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' restituisce un 'Elenco ' considerando che 'Dove' restituisce un' IEnumerable '. Ma 'Where' sta usando l'esecuzione posticipata, quindi lo si ottiene solo quando si _materializza_, per esempio. con 'ToList'. –

+5

Perché non l'hai postata come risposta? :) Una spiegazione migliore rispetto all'unica risposta qui, che è stata pubblicata più tardi del tuo commento. –

+0

Anche se il tuo commento ha risposto alla domanda, è stata inviata una risposta, che risponde in modo più completo rispetto a qualsiasi risposta nella domanda collegata. –

risposta

2

La mia ipotesi migliore sarebbe che succede qualcosa tra chiamare Dove, che crea un enumeratore e il luogo nel codice in cui siano effettivamente utilizzati i risultati (cioè dove MoveNext e (get_) Current di quell'enumeratore vengono effettivamente chiamati, ad es. da ToList).

+0

Il problema era che non ho chiamato .ToList() quando si utilizza Where() su IEnumerable, quindi ispezionarlo mostrava una lista vuota. –

1

Sì, dove è una versione lenta di findall. FindAll() è una funzione del tipo List, non è un metodo di estensione LINQ come Where. Il metodo FindAll su List, che è un metodo di istanza che restituisce una nuova lista con lo stesso tipo di elemento. FindAll può essere utilizzato solo su istanze Elenco mentre i metodi di estensione LINQ funzionano su qualsiasi tipo che implementa IEnumerable.

La differenza principale (oltre a quella su cui sono implementati: IEnumerable vs. List) è che Dove implementa l'esecuzione posticipata, in cui in realtà non effettua la ricerca finché non ne hai bisogno, (utilizzandola in un ciclo foreach per esempio). FindAll è un metodo di esecuzione immediato.

Mi riferirò a una struttura di dati chiamata albero di espressioni per comprendere l'esecuzione posticipata, basti pensare che un albero di espressioni è una struttura di dati come una lista o una coda. Mantiene una query LINQ to SQL non i risultati della query, ma gli elementi effettivi della query stessa.

per capire il Where di lavoro abbiamo bisogno di vedere che se si scrive un codice

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

query non esegue qui mentre esegue nel ciclo foreach

per capire LINQ and Deferred Execution