Disquisendo di LINQ presso alcuni clienti mi sono reso conto che molti sviluppatori non ne hanno ben chiaro il funzionamento percui ricorre spesso la frase “Bello, ma LINQ costringe a inutili iterazioni”.
Esempio: Prendiamo un array di interi e supponiamo di volere enumerare i valori >10 interrompendo il ciclo quando il valore contenuto è uguale a 12.
foreach (int v in values)
{
if (v > 10)
{
Console.WriteLine("Value is: {0}", v);
if (v > 12) break;
}
}
in questo caso al terzo elemento l'enumerazione si interrompe.
Con LINQ il codice precedente può essere ricondotto a:
IEnumerable<int> query = values.Where(v => v > 10)
.Select(v => v);
foreach (int value in query)
{
Console.WriteLine("Value is:{0}", value);
if (value == 12) break;
}
A questo punto la critica è: “Ok, ma Where enumera tutti gli elementi e passa a Select quelli che soddisfano il criterio”
In realtà non è cosi e per dimostrarlo aggiungiamo al nostro esempio una classe con gli stessi extension methods esposti da Enumerable che, essendo in scope, verrano usati al posto di quelli presenti in System.Linq:
public static class Extensions
{
public static IEnumerable<T> Where<T> (this IEnumerable<T> items, Func<T, bool> filter)
{
int count = 0;
Console.WriteLine("*Entering Where*");
foreach (T item in items)
{
Console.WriteLine("Where:{0}", count++);
if (filter(item)) yield return item;
}
}
public static IEnumerable<U> Select<T, U> (this IEnumerable<T> sequence, Func<T, U> selector)
{
int count = 0;
Console.WriteLine("*Entering Select*");
foreach (var item in sequence)
{
Console.WriteLine("Select: {0}", count++);
yield return selector(item);
}
}
}
Eseguendo il codice l'output ottenuto è il seguente:
dal quale si denota:
- La Select viene eseguita prima di Where.
- La sequenza non viene completamente enumerata.
- Ad ogni enumerazione dei risultati corrisponde uno step di enumerazione della query.
Seguendo step-by-step l'esecuzione della query il tutto è ancora più evidente.
In pratica, definendo Helper l'equivalente della classe Extensions che non fa uso di extension methods è come se scrivessimo:
IEnumerable<int> query=Helper.Select(Helper.Where(values,v=>v>10),v=>v);
Ovviamente questo non si applica nel caso si faccia uso di conversion operators tipo ToList();
Technorati Tags:
LINQ,
IEnumerable<T>