Perché dovrei usare List <T> su IEnumerable <T>?


24

Nella mia applicazione Web ASP.net MVC4 utilizzo IEnumerables, cercando di seguire il mantra per programmare l'interfaccia, non l'implementazione.

Return IEnumerable(Of Student)

vs

Return New List(Of Student)

Le persone mi dicono di usare List e non IEnumerable, perché gli elenchi impongono l'esecuzione della query e IEumerable no.

È davvero questa la migliore pratica? C'è qualche alternativa? Mi sento strano usando oggetti concreti in cui è possibile utilizzare un'interfaccia. Il mio strano sentimento è giustificato?


2
Innanzitutto, perché è una buona cosa forzare l'esecuzione della query? In secondo luogo, dovresti monitorare e profilare le chiamate al database per valutare se questa considerazione tecnica ha qualche merito.
user16764

2
Si dice che tutte le query dovrebbero essere eseguite in modo che il modello sia fatto e caricato pronto per la vista. Cioè la vista dovrebbe ricevere tutto e non interrogare il database.
Rowan Freeman,

3
Questa domanda StackOverflow la copre abbastanza bene.
Karl Bielefeldt,

Questa è una buona risposta, ma voglio sapere che è rilevante per MVC. Perché non è possibile assegnare a IEnumerables la vista in modo che tutte le query vengano eseguite just-in-time?
Rowan Freeman,

2
"Si dice che tutte le query dovrebbero essere eseguite in modo che il modello sia fatto e caricato pronto per la vista. Cioè la vista dovrebbe ricevere tutto e non interrogare il database.". Questa è una sciocchezza. Se passi un IEnumerable, la tua vista non sa o si preoccupa se sta eseguendo una query sul database. E così dovrebbe essere.
user16764

Risposte:


21

Ci sono momenti in cui l'esecuzione di una ToList()query su Linq può essere importante per garantire che le query vengano eseguite nel momento e nell'ordine previsto. Tali scenari sono tuttavia rari e nulla di cui uno dovrebbe preoccuparsi troppo fino a quando non si imbattono realmente in loro.

Per farla breve, utilizzare IEnumerableogni volta che è necessaria solo l'iterazione, utilizzare IListquando è necessario indicizzare direttamente e è necessario un array di dimensioni dinamiche (se è necessario indicizzare su un array di dimensioni fisse, utilizzare semplicemente un array standard).

Per quanto riguarda la cosa del tempo di esecuzione, puoi sempre usare un elenco come IEnumerablevariabile, quindi sentiti libero di restituire un IEnumerablefacendo un .ToList();, o passa un parametro come IEnumerableeseguendo .ToList()il IEnumerableper forzare l'esecuzione in quel momento. .ToList()Fai solo attenzione che ogni volta che imponi l'esecuzione con te non ti aggrappi alla IEnumerablevariabile per la quale l'hai appena fatto ed eseguirla di nuovo, altrimenti finirai per raddoppiare inutilmente le iterazioni nella tua query LINQ.

Per quanto riguarda MVC, non c'è davvero nulla di speciale da notare qui. Seguirà le stesse regole sui tempi di esecuzione del resto di .NET, penso che potresti avere qualcuno che è stato un po 'confuso a causa della semantica di esecuzione ritardata in passato e incolpato su MVC dicendo che questo è in qualche modo correlato, ma è non. La semantica dell'esecuzione ritardata confonde all'inizio tutti (e anche per un bel po 'di tempo dopo; possono essere un po' complicati). Ancora una volta, però, non ti preoccupare fino a quando non ti preoccupi davvero di garantire che una query LINQ non venga eseguita due volte o richieda che venga eseguita in un certo ordine rispetto ad altro codice, a quel punto assegna la tua variabile a se stessa. per forzare l'esecuzione e starai bene.


È male dare una vista a IEnumerables? Dovresti dare Elenchi in modo che le query siano state eseguite al momento della visualizzazione?
Rowan Freeman,

@RowanFreeman Ho appena aggiunto una modifica per rispondere a questa domanda. Penso che tu abbia qualcuno che ha incontrato qualcosa che non capiva del tutto (non può biasimarlo, l'esecuzione ritardata è seriamente complessa e confusa) e lo ha assegnato a un cattivo joojoo invece di lavorare per capire il comportamento completo dell'esecuzione ritardata semantica.
Jimmy Hoffa,

Bella risposta. Quindi dovrò mai realmente usare .ToList ()? Finora la mia applicazione funziona bene usando solo IEnumerables e passandoli dal modello alla vista. Nessun elenco o .ToList (). La mia domanda non riguarda le funzionalità: so che la mia applicazione funziona. La mia domanda è una delle migliori pratiche.
Rowan Freeman,

4
La best practice di @RowanFreeman è utilizzare l'interfaccia minima che soddisfa ancora le tue esigenze. IEnumerable lo sta facendo proprio ora, quindi non preoccuparti di cambiarlo. Detto questo, arriverà un giorno in cui avrai una query in esecuzione 3 o 10 volte e non capirai perché, o aspettandoti che una query venga eseguita prima di un inserimento solo per trovarla dopo, questi sono i tempi che devi riconoscere. () forzerà l'esecuzione quando vuoi, e reiterando un IEnumerable da una query LINQ più volte eseguirà l'intera query più volte; Risolvi la tua esecuzione quando accadono quegli eventi
Jimmy Hoffa,

1
Non dovresti nemmeno usare - Listpassare intorno a Listimplica che il contenuto dell'elenco verrà modificato. Se si desidera restituire una raccolta, utilizzare IReadOnlyCollection. Listè da utilizzare nei metodi e per lo scambio tra i metodi che modificano l'elenco. Questo è tutto!
ErikE,

7

Ci sono due problemi.

IENumerable<Data> query = MyQuery();

//Later
foreach (Data item in query) {
  //Process data
}

Quando viene raggiunto il ciclo "Dati di processo", la query potrebbe non essere più valida. Ad esempio, se la query viene eseguita su un DataContext che è già stato eliminato, il codice genererà un'eccezione. Questo tipo di cose diventa molto confuso quando si elabora una query in un contesto diverso da quello in cui è stata creata.

Un problema secondario è che la connessione non verrà rilasciata fino al completamento del ciclo "Dati di processo". Questo è solo un problema se "Dati di processo" è complesso. Questo è menzionato su http://msdn.microsoft.com/en-us/library/bb386929.aspx :

D. Per quanto tempo rimane aperta la mia connessione al database?

R. Una connessione in genere rimane aperta fino a quando non si consumano i risultati della query. Se si prevede di impiegare del tempo per elaborare tutti i risultati e non si oppone alla memorizzazione nella cache dei risultati, applicare ToList alla query. In scenari comuni in cui ogni oggetto viene elaborato una sola volta, il modello di streaming è superiore sia in DataReader sia in LINQ to SQL.

Quindi, questi problemi sono il motivo per cui vieni incoraggiato a garantire che la query sia effettivamente eseguita, ad esempio chiamando ToList(). Tuttavia, come suggerisce Jimmy, non c'è nulla che ti impedisca di restituire la tua lista come IEnumerable.

Come regola generale, raccomando di evitare l'iterazione su un IEnumerable più di una volta. Supponendo che i consumatori del tuo codice seguano questa regola, non considero preoccupante che qualcuno possa colpire il database due volte eseguendo la query due volte.


1

Un altro vantaggio dell'enumerazione IEnumerableanticipata è che verranno generate eccezioni nella posizione appropriata. Questo aiuta il debug.

Ad esempio, se hai un'eccezione deadlock all'interno di una delle tue viste Razor, non sarebbe davvero chiaro come se l'eccezione si verificasse durante uno dei tuoi metodi di accesso ai dati.


Qualsiasi metodo che restituisce un oggetto IEnumerableche può essere lanciato probabilmente fa un errore. I IEnumerablemetodi differiti dovrebbero essere divisi in due: un metodo non differito controlla i parametri e imposta, lanciando se necessario (diciamo, a causa di un argomento nullo). Quindi restituisce una chiamata all'implementazione privata che è rinviata. Non penso che i miei commenti siano completamente in contrasto con la tua risposta, ma penso che tu abbia lasciato fuori un aspetto importante nella tua risposta, che è il significato semantico di usare IEnumerablevs. a List(mutazione) vs.IReadOnlyCollection (nessun beneficio per differimento) .
ErikE,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.