Cominciamo ad analizzare oggi il metodo LinqToObjects Select. Vediamo i due overload presenti

 

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
       
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector)

 

Iniziamo la nostra analisi dal secondo overload che ha l'implementazione più semplice. Diversamente dal metodo Where i due metodi Select hanno 2 parametri generici, il primo "TSource" è determinato dall'oggetto su cui stiamo chiamando il metodo mentre il parametro "TResult" sarà determinato dal delegato che andremo a passare al metodo. Nell'utilizzo più comune il metodo sarà sempre passato tramite l'utilizzo di una lambda expression ma ricordiamo che non è l'unico modo per farlo.

Il secondo overload è implementato nel modo seguente 

 

    public static class Enumerable
    {
        // altro codice

        public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
        {
            if (source == null) throw Error.ArgumentNull("source");
            if (selector == null) throw Error.ArgumentNull("selector");
            return SelectIterator<TSource, TResult>(source, selector);
        }

        static IEnumerable<TResult> SelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
        {
            int index = -1;
            foreach (TSource element in source)
            {
                checked { index++; }
                yield return selector(element, index);
            }
        }

        // altro codice
    }

 Come sempre nei metodi che utilizzano lo yield return per effettuare una validazione efficace dei parametri in ingresso è necessario spezzare il metodo in due:

  • Metodo pubblico che effettua la validazione e chiama un metodo privato
  • Il metodo privato che contiene il codice del metodo e viene chiamato dal metodo pubblico

Esattamente questo il pattern che troviamo nel metodo Select, l'extension method pubblico effettua la validazione dei due argomenti source e selector per poi chiamare il metodo privato SelectIterator che contiene l'effettiva implementazione del metodo.

La lambda expression di "proiezione" di TSource su TResult è una funzione che dipende anche da un intero che sarà automaticamente valorizzato con l'indice (a partire da zero) dell'elemento su cui stiamo chiamando la funzione rispettivamente alla successione source.

Nessun tentativo di ottimizzazione viene effettuato e l'esecuzione del metodo è la stessa indipendentemente dalla classe che implementa l'interfaccia IEnumerable<T> passata come parametro source.

Un indice "index" viene istanziato fuori dal ciclo che si occuperà di incrementare l'indice e chiamare la funzione di proiezione sull'elemento corrente dell'iterazione. Osserviamo che questo metodo andrà in eccezione con successioni più lunghe di Int32.MaxValue a causa dell'incremento all'interno di un blocco checked.

comments powered by Disqus