linq
Modalità di esecuzione del metodo: streaming immediato, differito, differito non streaming
Ricerca…
Esecuzione differita vs esecuzione immediata
Alcuni metodi LINQ restituiscono un oggetto query. Questo oggetto non contiene i risultati della query; invece, ha tutte le informazioni necessarie per generare quei risultati:
var list = new List<int>() {1, 2, 3, 4, 5};
var query = list.Select(x => {
Console.Write($"{x} ");
return x;
});
La query contiene una chiamata a Console.Write
, ma non è stato prodotto alcun output sulla console. Questo perché la query non è stata ancora eseguita, e quindi la funzione passata a Select
non è mai stata valutata. Questa operazione è nota come esecuzione differita: l'esecuzione della query viene posticipata fino a un momento successivo.
Altri metodi LINQ forzano un'esecuzione immediata della query; questi metodi eseguono la query e ne generano i valori:
var newList = query.ToList();
A questo punto, la funzione passata in Select
verrà valutata per ogni valore nell'elenco originale, e quanto segue verrà emesso sulla console:
1 2 3 4 5
In genere, i metodi LINQ che restituiscono un singolo valore (come Max
o Count
) o che restituiscono un oggetto che contiene effettivamente i valori (come ToList
o ToDictionary
) vengono eseguiti immediatamente.
I metodi che restituiscono un oggetto IEnumerable<T>
o IQueryable<T>
restituiscono l'oggetto query e consentono di posticipare l'esecuzione fino a un punto successivo.
Se un particolare metodo LINQ impone una query da eseguire immediatamente o meno, è possibile trovarlo su MSDN - C # o VB.NET .
Modalità streaming (valutazione lazy) vs modalità non streaming (valutazione stimolante)
Tra i metodi LINQ che utilizzano l'esecuzione differita, alcuni richiedono un singolo valore da valutare alla volta. Il seguente codice:
var lst = new List<int>() {3, 5, 1, 2};
var streamingQuery = lst.Select(x => {
Console.WriteLine(x);
return x;
});
foreach (var i in streamingQuery) {
Console.WriteLine($"foreach iteration value: {i}");
}
produrrà:
3
foreach valore di iterazione: 3
5
foreach valore di iterazione: 5
1
foreach valore di iterazione: 1
2
foreach valore di iterazione: 2
poiché la funzione passata a Select
viene valutata ad ogni iterazione di foreach
. Questo è noto come modalità di streaming o valutazione lazy .
Altri metodi LINQ - operatori di ordinamento e raggruppamento - richiedono che tutti i valori siano valutati, prima che possano restituire qualsiasi valore:
var nonStreamingQuery = lst.OrderBy(x => {
Console.WriteLine(x);
return x;
});
foreach (var i in nonStreamingQuery) {
Console.WriteLine($"foreach iteration value: {i}");
}
produrrà:
3
5
1
2
foreach valore di iterazione: 1
foreach valore di iterazione: 2
foreach valore di iterazione: 3
foreach valore di iterazione: 5
In questo caso, poiché i valori devono essere generati nel foreach
in ordine crescente, tutti gli elementi devono prima essere valutati, per determinare quale è il più piccolo, e che è il successivo più piccolo, e così via. Questo è noto come modalità non streaming o valutazione entusiasta .
Se un particolare metodo LINQ utilizza la modalità streaming o non streaming, è possibile trovarlo su MSDN - C # o VB.NET .
Vantaggi dell'esecuzione posticipata: creazione di query
L'esecuzione posticipata consente di combinare diverse operazioni per creare la query finale, prima di valutare i valori:
var list = new List<int>() {1,1,2,3,5,8};
var query = list.Select(x => x + 1);
Se eseguiamo la query a questo punto:
foreach (var x in query) {
Console.Write($"{x} ");
}
otterremmo il seguente risultato:
2 2 3 4 6 9
Ma possiamo modificare la query aggiungendo più operatori:
Console.WriteLine();
query = query.Where(x => x % 2 == 0);
query = query.Select(x => x * 10);
foreach (var x in query) {
Console.Write($"{x} ");
}
Produzione:
20 20 40 60
Vantaggi dell'esecuzione posticipata - interrogazione dei dati correnti
Con l'esecuzione posticipata, se i dati da interrogare vengono modificati, l'oggetto query utilizza i dati al momento dell'esecuzione, non al momento della definizione.
var data = new List<int>() {2, 4, 6, 8};
var query = data.Select(x => x * x);
Se eseguiamo la query a questo punto con un metodo immediato o foreach
, la query opererà nell'elenco dei numeri pari.
Tuttavia, se cambiamo i valori nell'elenco:
data.Clear();
data.AddRange(new [] {1, 3, 5, 7, 9});
o anche se assegniamo una nuova lista ai data
:
data = new List<int>() {1, 3, 5, 7, 9};
e quindi eseguire la query, la query opererà sul nuovo valore dei data
:
foreach (var x in query) {
Console.Write($"{x} ");
}
e mostrerà quanto segue:
1 9 25 49 81