Inauguriamo con questo post una serie su LinqToObject (da adesso in poi Linq) in cui andremo a studiare i sorgenti per capire in che modo questa funzionalità è stata implementata.

Per chi non lo conoscesse Linq permette di eseguire numerose operazioni su delle collezioni di elementi tra cui:

-          Filtrare

-          Proiettare

-          Medie e somme

-          Intersezioni e unioni

La maggior parte dei metodi Linq sono implementati come extension methods sull’interfaccia generica IEnumerable<T>. Quest’interfaccia eredita dalla corrispettiva non generica IEnumerable ed espone un’unico metodo GetEnumerator() che restituisce un IEnumerator<T>.

L’interfaccia IEnumerable permette di eseguire una foreach sulle classi che la implementano e quindi in un certo senso di “estrarre” i valori da una collezione di elementi. La foreach infatti semplifica e nasconde un meccanismo di iterazione leggermente più sofisticato di quello che sembra e che fa uso dell’istanza dell’IEnumerator<T> restituita dal metodo GetEnumerator().

Per capire in cosa viene tradotta una foreach dal compilatore C# guardiamo allo snippet seguente

 

var lista = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

foreach (var item in lista)
{

}

using (var enumerator = lista.GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        var item = enumerator.Current;
    }
}

 

La lista di interi è una classe che implementa l’interfaccia IEnumerable<int> e per questo motivo possiamo eseguire una foreach. Quello che succede realmente è che sulla lista viene chiamato il metodo GetEnumerator() e l’istanza dell’enumeratore è in grado di dirci se c’è un altro elemento ed eventualmente quale.

In particolare non sappiamo neanche se ci sia almeno un elemento per cui è indispensabile per cominciare ad iterare chiamare come prima cosa il metodo MoveNext(). Nella pratica un IEnumerator<T> permette di scorrere una lista e ha la responsabilità di:

-          Verificare la presenza di ulteriori elementi nella lista

-          Restituire l’elemento corrente

-          Ricominciare da capo l’iterazione (metodo Reset)

-          Invalidare l’iterazione se la successione di elementi sottostante è stata modificata

 

Implementare un iteratore per una classe può essere un’operazione delicata e difficile, il compilatore C# però con l’utilizzo della funzionalità “yield return” permette di creare iteratori e implementare l’interfaccia IEnumerable in maniera estremamente semplice. Nel post successivo vedremo come fare.

comments powered by Disqus