Recentemente ho assistito ad una serie di incontri organizzati dall' XPLabs, introduttivi al mondo dell XP.
Indipendentemente dall effettivo o meno miglioramento che si ottiene seguendo certi canoni ho voluto un attimo approfondire alcuni punti che erano rimasti in sospeso.
In particolare non avevamo approfondito la metodologia da applicare per eliminare i for dal codice,
e cosi ho trovato questa presentazione in powerpoint che spiega come farlo in Java.

Sinceramente prima di leggerlo mi ero già fatto un idea di come si potessero eliminare in c# e la soluzione illustrata non dista molto da quella che avevo pensato io che a uso dei delegati.

Comunque siccome quella slide ha ispirato questo articoletto, ho voluto citarla per correttezza.

Una soluzione piu simile a quella proposta si può ottenere molto semplicemente usando le nuove estensioni del linguaggio offerte dal nuovo compilatore c#
in particolare in questa prima parte vedremo come utilizzare le lambda expression e gli extension method per ottenere un codice "parlante" ,
libero da cicli, che ricorderà molto da vicino un listato ruby :D

prendiamo il seguente codice:


    public static void DoSomething(){
   
        // inizializzazione e popolamento di una collezione
        string[] pippo = new string[]{"a","b","c"};
   
        // per ogni elemento (iterazione)
        for(int i=0,max=pippo.Length;i<max;i++){
               
            // selezione
            if(pippo[i] != "b"){
               
                // output
                MessageBox.Show(pippo[i]);
            }
        }
    }
   
in poche righe abbiamo del codice che fa tante cose, ovvero
1) inizializzazione di una collezione
2) iterazione all interno della collezione
3) selezione di determinati elementi della collezione
4) output a video

Partiamo con generalizzare la funzione anzichè usare un array di stringhe (in futuro potrei volere un altro contenitore di stringhe...)


    public static void DoSomething()
    {
        // inizializzazione e popolamento di una collezione
        IEnumerable pippo = (IEnumerable)new string[] { "a", "b", "c" };

        // iterazione
        for (IEnumerator iter = pippo.GetEnumerator(); iter.MoveNext(); )
        {
            // fetch
            string tmp = iter.Current as string;

            // selezione
            if (tmp != "b")
            {
                // output
                MessageBox.Show(tmp);
            }
        }
    }


ok fino a qui nulla di strano, il codice è piu generico e abbiamo introdotto uno step aggiuntivo che è il fetch del dato corretto dalla collezione.
Vorrei partire da questo codice per migliorarlo, eliminando il for...

Mi viene in mente che tra le novità introdotte nel javascript 1.8 c'è una funzione aggiunta agli Array che si chiama reduce e che prende in input una funzione che torna vero o falso,
a cui reduce passera ogni elemento dell array e ritorna una Array scremato in base al risultato,
mi piacerebbe ottenere un qualcosa di simile..

il codice che vorrei ottenere è un qualcosa di simile a:

IEnumerable collection = pippo.Reduce(mioDelegatoDiSelezione);

...già che ci siamo ci vorrebbe un metodo Each che per ogni elemento facesse qualcosa..

Il codice che vorrei ottenere e' dunque somigliante a :

pippo.Reduce(mioDelegatoDiSelezione).Each(mioDelegatoDiOutput);


Qui un classico programmatore OO penserebbe a come poter estendere pippo senza violare l' information hiding...

"Il Vero Programmatore" C# invece è al corrente delle innovazioni apportate al compilatore 3.0,
in particolare delle "extension method", che permettono di estendere una classe base ... senza realmente estenderla,
infatti gli extension method non sono null' altro che del "syntactic sugar" introdotto nel compilatore che permette di scrivere anzichè:

MetodoStatico (istanzaOggettoBase, parametri...);

direttamente

istanzaOggettoBase.MetodoStatico(parametri...);

dando dunque l impressione di aver esteso una classe :D

quindi creeremo in un diverso namespace una classe statica e pubblica (che chiameremo ad esempio ExtensionMethods):


public static class ExtensionMethods{
    public static IEnumerable Reduce(this IEnumerable me, Func<object,bool> rule){
   
        // creo una lista che conterra' l IEnumerable risultante
        List<object> result = new List<object>();
       
        // itero sulla collezione (non sto eliminando il for, ma a mio avviso non occorre :D )
        for(IEnumerator iter = me.GetEnumerator();iter.MoveNext(); ){
           
            // salvo l oggetto in una variabile temporanea per caching e comodita' ;D
            object tmp = iter.Current;
           
            // controllo se l oggetto passa la regola
            if(rule(tmp)){
           
                // aggiungo nella collezione finale l oggetto
                result.Add(tmp);
            }
        }
       
        // ritorno la lista scremata
        return result;
    }
}

adesso abbiamo esteso IEnumerable aggiungendo un metodo Reduce che vuole in ingresso un oggetto di tipo
Func<object,bool>, ovvero un delegato di un metodo che vuole in ingresso un oggetto e restituisce un valore booleano

In questo momento utilizzando le lambda expression possiamo scrivere:


    public static void DoSomething()
    {
        IEnumerable pippo = (IEnumerable)new string[] { "a", "b", "c" };
       
        //per ogni elemento della lista scremata
        foreach (string tmp in pippo.Reduce(
            meBoxed =>{
                var me = meBoxed as string;
                return me != "b";
            })
        ){
            //mostralo a video
            MessageBox.Show(tmp);
        }

    }

Ora pero' aggiungiamo anche un extension Method Each


public static void Each(this IEnumerable me, Action<object> action){

    //ciclo sulla lista
    for(IEnumerator iter = me.GetEnumerator();iter.MoveNext(); ){
   
        //applico l azione all elemento della lista
        action(iter.Current);
    }
}

..da notare che ora volendo potremo riscrivere Reduce come:


public static IEnumerable Reduce(this IEnumerable me, Func<object,bool> rule){   
    List<object> result = new List<object>();
       
    me.Each(item=>{
        if(rule(item)){
            result.Add(item);
        }
    });   
    return result;
}

..si commenta da sola!

bene ora il codice di DoSomething diventerà :


public static void DoSomething(){
   
    IEnumerable pippo = (IEnumerable)new string[]{"a","b","c"};   
   
    pippo.Reduce(meBoxed =>{
        var me = meBoxed as string;
        return(me != "b");
    }).Each(item =>{MessageBox.Show(item);});
   
}

E ancora meglio:


    private Func<object,bool> excludeStringB = meBoxed =>{
        var me = meBoxed as string;
        return me != "b";
    };

    private Action<object> showMessageBox = item =>{MessageBox.Show(item);};


    public static void DoSomething()
    {
        IEnumerable pippo = (IEnumerable)new string[] { "a", "b", "c" };
        pippo.Reduce(excludeStringB).Each(showMessageBox);
    }

volendo si possono spostare le 2 lambda in 2 classi diverse (ad esempio Filter e Action), ma per questo esempio credo possano bastare cosi :D

svantaggi di questa tecnica?
Stiamo ciclando 2 volte sulla stessa collection, quindi c'e' sicuramente uno svantaggio in termini di prestazioni
gli oggetti tra di loro hanno ancora un legame forte, vedremo nel prossimo post riguardante la IOC come poter separare
la logica di creazione definendo le dipendenze esternamente (utilizzando NInject ;) )