Come funziona la seguente istruzione LINQ?


160

Come funziona la seguente istruzione LINQ ?

Ecco il mio codice:

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0);
list.Add(8);
foreach (var i in even)
{
    Console.WriteLine(i);
}

Produzione: 2, 4, 6, 8

Perché no 2, 4, 6?


102
Il risultato di un'espressione di query è una query, non l'esecuzione della query.
Eric Lippert,

6
Per meno informazioni, vedi la risposta accettata a questa domanda .
Daniel,

9
Sicuramente puoi pensare a un titolo che sintetizza effettivamente la domanda.
Matt Ball,

2
La mia ipotesi sui voti negativi (6 ormai, non il mio) è che considerano il titolo della domanda troppo generico per essere una buona domanda. Ma, vedendo il numero di voti e diventando la domanda principale della settimana nella newsletter, non penso che tu debba preoccuparti troppo.
Abel,

Risposte:


235

L'output è 2,4,6,8dovuto all'esecuzione differita .

La query viene effettivamente eseguita quando la variabile della query viene ripetuta, non quando viene creata la variabile della query. Questo si chiama esecuzione differita.

- Suprotim Agarwal, "Esecuzione di query differita vs immediata in LINQ"

Esiste un'altra esecuzione chiamata Immediate Query Execution , utile per la memorizzazione nella cache dei risultati della query. Da Suprotim Agarwal di nuovo:

Per forzare l'esecuzione immediata di una query che non produce un valore singleton, è possibile chiamare il metodo ToList(), ToDictionary(), ToArray(), Count(), Average()o Max()su una query o variabile di query. Questi sono chiamati operatori di conversione che ti permettono di fare una copia / istantanea del risultato e l'accesso è quante volte vuoi, senza la necessità di rieseguire la query.

Se vuoi che l'output sia 2,4,6, usa .ToList():

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0).ToList();
list.Add(8);
foreach (var i in even)
 {
    Console.WriteLine(i);
 }

8
Count (), Max (), Avg (), Sum () e probabilmente altri metodi che devono prendere in considerazione l'intero elenco, causano anche la valutazione della query.
Kenned,

1
Ho spesso pensato di avere, per esempio, 'filteredList' come una variabile, piuttosto che 'filterList ()' come metodo: l'idea è che tu ci provi ogni volta che vuoi filtrare l'elenco, piuttosto che chiamare un metodo. Potrebbe essere un metodo interessante, anche se insolito e forse imperfetto, per quanto riguarda le prestazioni.
Katana314,

4
@Sebastian - A seguito @ commento di Kenned, .First(), .FirstOrDefault(), .Single()e .SingleOrDefault()anche innescare la valutazione della query.
Scotty.NET,

4
stupefacente come hai ottenuto la risposta in meno di 30 secondi: D
MC

2
@MC Non so perché stai facendo questa domanda. L'intera risposta non è stata data alla volta. È stato modificato più volte.
Atish Dipongkor - MVP,

11

Ciò è accaduto a causa di un'esecuzione differita, il che significa che il calcolo dell'espressione non viene eseguito fino a quando non è necessario da qualche parte. Ciò migliora le prestazioni se i dati sono troppo grandi.


3
Potresti sfumarlo, poiché può anche significare che la tua costosa enumerazione viene eseguita più volte. In tal caso, potresti persino subire una perdita di prestazioni.
Smorfia di disperazione,

0

La ragione di ciò è l'esecuzione differita della tua espressione lambda. La query viene eseguita quando si avvia l'iterazione nel ciclo foreach.


11
Tecnicamente è l'esecuzione differita dell'iteratore , non della lambda .
D Stanley,

0

Quando si utilizza un IEnumerable <> ottenuto da LINQ, viene creata solo una classe Enumerator e l'iterazione inizia solo quando lo si utilizza in alcuni passi.


-1

Stai ottenendo questo risultato a causa di un'esecuzione differita, il che significa che il risultato non viene effettivamente valutato fino al suo primo accesso.

Per rendere più chiaro basta aggiungere 10 all'elenco alla fine del tuo snipet e quindi stampare di nuovo non otterrai 10 in uscita

     var list = new List<int>{1,2,4,5,6};
    var even = list.Where(m => m%2 == 0).Tolist();
    list.Add(8);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }
//new*
    list.Add(10);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }

Ci hai provato davvero? Ottengo 10l'output.
Mark Hurd,

buona cattura @MarkHurd sì non aggiunto .ToList (). modificato il post ora dovrebbe dare l'output previsto. La mia aspettativa era che l'espressione fosse valutata solo quando si utilizza var per la prima volta, ma sembra che venga valutata ogni volta
sandeep

Ora non conterrà 8in alcun output.
Mark Hurd,
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.