Se avessimo sotto mano un linguaggio funzionale (ad esempio ), potremmo gestire in maniera facilissima una collection o una lista.
Con il Framework , possiamo lavorare ugualmente in maniera più funzionale con i (Approfondimento). Utilizzando questa feature del possiamo elaborare i nostri dati in maniera molto più immediata, specialmente grazie ai (anonymous delegates).

Un esempio che voglio riportare, ed è anche il più frequente che mi è capitato di sviluppare, è l’utilizzo di questi delegate per la funzione di ricerca di una lista generica ()

Invece di scrivere il codice con cicli foreach ogni volta che ho bisogno di trovare un elemento in un insieme, utilizzo un predicato che fa il lavoro al mio posto, così posso mantenere il codice più pulito e concentrarmi su “cose” più interessanti.

Il modo più semplice per utilizzare il metodo Find() con un Predicato è riportato in questo codice :

public class person
{
    public int Anni { get; set; }
    public string Nome { get; set; }
    public person(int anni, string nome)
    {
        this.Anni = anni;
        this.Nome = nome;
    }
}

 

//si presuppone di avere una lista non vuota
List<person> agenda = new List<person>();
/* .. load objects from DataBase .. */

//creo un semplicissimo criterio di ricerca
string personaDaCercare = "mario";
person mario = agenda.Find(delegate(person p) { return p.Nome == personaDaCercare; });

SanDisk Mobile Ultra

Sfruttando invece le () introdotte dal Framework , il codice si riduce ancora di più:

//si presuppone di avere una lista non vuota
List<person> agenda = new List<person>();
/* .. load objects from DataBase .. */

//creo un semplicissimo criterio di ricerca
string personaDaCercare = "mario";
person mario = agenda.Find(p =&gt; p.Nome == personaDaCercare;);

È anche possibile scrivere un predicato esplicito se proprio non ci piace vedere codice troppo stringato:

private static bool cercaMinorenni(person p)
{
    if (p.Anni &lt; 18)
        return true;
    return false;
}

List<person> BlockBuster_GameOnly = agenda.findAll(cercaMinorenni);

Raffinando ancora di più il codice, per sfruttare questa metodologia non solo per il Find(), ma anche per altre casistiche, possiamo ampliare la classe effettuando l’override del metodo Equals come in questo altro esempio C#:

public class person
{
    public int Anni { get; set; }
    public string Nome { get; set; }
    public person(int anni, string nome)
    {
        Anni = anni;
        Nome = nome;
    }
    public override bool Equals(object obj)
    {
        if (obj == null) return false;

        person p2 = obj as person;
        if ((object)p2 == null) return false;

        return (this.Anni == p2.Anni) && (this.Nome == p2.Nome);
        //return (this.Anni.Equal(p2.Anni)) && this.Nome.Equal(p2.Nome));
    }
    public override bool Equals(person p2)
    {
        if ((object)p2 == null) return false;

        return (this.Anni == p2.Anni) && (this.Nome == p2.Nome);
        //return (this.Anni.Equal(p2.Anni)) && this.Nome.Equal(p2.Nome));

    }
    public override int GetHashCode()
    {
        return this.Anni ^ this.Nome.Length;
    }
}

In questa maniera possiamo non solo cercare per un parametro particolare, ma cercare anche “entità” specifiche all’interno di una lista:

person Glauco = new person(36, "Glauco");
if (agenda.Find(p => p.Equals(Glauco)) == null)
    agenda.Add(Glauco);

Come potete vedere dal codice, può essere molto intuitivo e veloce scrivere espressioni Lambda in casi semplici come questi, ma cosa succede se abbiamo liste che contengono oggetti molto complessi? Ad esempio un oggetto che identifica un testCrash di un’autovettura, o uno che rappresenta una rilevazione su un impianto di controllo numerico per una catena di montaggio, piuttosto per rilevazione dati su un impianto gas di una centrale.
Questi oggetti potrebbero essere molto complessi ed il codice diventare quasi illeggibile.
Meglio optare per la versione con predicati espliciti. Ma anche in questo caso dovremmo scrivere un predicato diverso per ogni criterio di ricerca o per ogni criterio di ordinamento che ci può servire.

Risolviamo creando una “classe predicato” che ammette parametri ed espone il metodo di ricerca, in modo da poter ridurre drasticamente i predicati da dover scrivere (anche se sarà praticamente impossibile ridurci ad un unico). Vediamo anche in questo caso un semplice (per non complicare troppo questo articolo) esempio scritto sempre in C#:

private class EnvironmentMonitoringResult
{
    //...
    //...
    //...
}

private enum CostructormanagerType
{
    gasMetano = 0,
    gasButano,
    //...
    //...
}

private class myPredicate
{
    private CostructormanagerType impianto;
    private bool dummy;
    private string Parametro1;
    //...
    //...
    //...

    public myPredicate(CostructormanagerType type)
    {
        this.impianto = type;
    }
    public myPredicate(CostructormanagerType type, bool isDummy)
    {
        this.impianto = type;
        this.dummy = isDummy;
    }
    public bool findTipo1(EnvironmentMonitoringResult risultatoMisurazione)
    {
        bool findOK = true;
        switch (this.impianto)
        {
            case CostructormanagerType.gasMetano:
                findOK &= risultatoMisurazione.isNotturno = this.dummy;
                findOK &= risultatoMisurazione.Parametro1 = this.Parametro1;
                //...
                //altri controlli
                break;
            case CostructormanagerType.gasButano:
                break;
            default:
                break;
        }
        //...
        //altri controlli
        return findOK;
    }
    public bool findTipo2(EnvironmentMonitoringResult risultatoMisurazione)
    { ... ... }
}

List<EnvironmentMonitoringResult> misurazioniMensili = new List<EnvironmentMonitoringResult>();

myPredicate ricercaCaso1 = new myPredicate(CostructormanagerType.gasButano, false);

//ricercaCaso1.ParametroX = qualcheCosa
EnvironmentMonitoringResult controlloAllarmeNotturno = misurazioniMensili.Find(ricercaCaso1.findTipo1);

//ricercaCaso1.ParametroY = qualcheCosaAltro
//ricercaCaso1.ParametroZ = stoInventando
EnvironmentMonitoringResult controlloCrashZonaAlfaX = misurazioniMensili.Find(ricercaCaso1.findTipo2);

Potete vedere che ricerche anche complesse possono essere racchiuse in una unica classe ed implementate magari dentro un Layer apposito (EnvironmentService ad esempio), separato dall’eventuale interfaccia o BusinessLogic

Spunti di studio

Così come abbiamo usato i delegati anonimi per il metodo Find() e FindAll(), possiamo espandere la nostra classe implementando l’interfaccia e utilizzare i predicati ( o espressioni Lambda) anche per il metodo Sort().