Liste di oggetti tipizzate ed ordinabili

Se ritieni utile questo articolo, considera la possibilità di effettuare una donazione (il cui importo è a tua completa discrezione) tramite PayPal. Grazie.

Creare liste di oggetti fortemente tipizzate ed ordinabili sulla base di una proprietà dell'entità contenuta nella lista stessa è una necessità molto frequente quando si utilizzano custom entities (business objects). In questo articolo viene proposta una possibile implementazione, sufficientemente generica e semplice.

Le classi di base

Per semplificare la creazione di collection tipizzate il framework ci mette a disposizione la classe astratta System.Collections.CollectionBase: derivando da questa un'altra classe astratta (SortableCollectionBase) possiamo aggiungere la possibilità di ordinare la lista, esponendo (ed implementando!) il metodo Sort. Per ordinare oggetti generici sulla base di una data proprietà, la classe ObjectComparer utilizzerà reflection, così da non dover specializzare i criteri di confronto per ogni specifica entità.
Per una maggior comprensione, si veda il class diagram seguente:

SortableCollectionBase - Class diagram

SortableCollectionBase

La classe astratta SortableCollectionBase, sarà il punto di partenza per la generazione di liste tipizzate:

using System;
using System.Collections;

namespace G4N.Common
{
    public abstract class SortableCollectionBase : CollectionBase
    {
        public void Sort(string propertyname)
        {
            this.Sort(propertyname, true);
        }
        
        public void Sort(string propertyname, bool ascending)
        {
            base.InnerList.Sort(new ObjectComparer(propertyname, ascending));
        }
    }
}

Come possiamo vedere, alla classe base CollectionBase viene aggiunto semplicemente il metodo Sort (sottoposto ad overload per la definizione del criterio di ordinamento). L'implementazione del metodo è comune a tutte le liste e sfrutta la classe ObjectComparer descritta nel prossimo paragrafo.

ObjectComparer

La classe ObjectComparer implementa, usando reflection, il confronto generico tra oggetti sulla base di una loro proprietà:

using System;
using System.Collections;
using System.Reflection;

namespace G4N.Common
{
    public class ObjectComparer : IComparer
    {
        private string m_propertyname;
        private bool m_ascending;

        public ObjectComparer(string propertyname, bool ascending)
        {
            m_propertyname = propertyname;
            m_ascending = ascending;
        }
        
        public int Compare(object x, object y)
        {
            Type tx = x.GetType();
            Type ty = y.GetType();

            PropertyInfo px = tx.GetProperty(m_propertyname);
            PropertyInfo py = ty.GetProperty(m_propertyname);

            IComparable vx = (IComparable)px.GetValue(x, null);
            object vy = py.GetValue(y, null);

            int result = vx.CompareTo(vy);
            if (result != 0)
            {
                if (m_ascending)
                    return result;
                else
                    return -result;
            }
            return 0;
        }
    }
}

Creare una lista tipizzata ed ordinabile

Una volta che abbiamo definito le classi di base, passiamo all'implementazione di una classe d'esempio e analizziamone il codice:

using System;
using G4N.Common;

namespace Demo
{
    [Serializable]
    public class UserList : SortableCollectionBase
    {

        public UserList() { }

        public int Add(User value)
        {
            return base.List.Add(value as object);
        }

        public User this[int index]
        {
            get { return (base.List[index] as User); }
        }

        public void Remove(User value)
        {
            base.List.Remove(value as object);
        }

        public void Insert(int index, User value)
        {
            base.List.Insert(index, value as object);
        }

        public bool Contains(User value)
        {
            return base.List.Contains(value as object);
        }

    }
}

L'implementazione di UserList (lista ordinabile di oggetti di tipo "User", classe ipotetica, omessa nell'esempio) ha semplicemente specializzato i metodi principali (lettura, iterazione, aggiunta, rimozione e verifica degli elementi contenuti) di accesso alla lista base (base.List), forzandone il tipo a User

In modo analogo è possibile definire ogni altro tipo di custom collection, semplicemente modificando quanto evidenziato in grassetto nel codice precedente, ovvero:

  • specificando il namespace in cui inserire la classe lista (nell'esempio: "Demo")

  • definendo il nome della classe lista (nell'esempio: "UserList")

  • modificando il tipo di entità gestita (nell'esempio: "User")

Utilizzando la tecnologia "Code Snippet" degli IDE Microsoft è possibile automatizzare il processo di generazione di classi custom che derivino da SortableCollectionBase. Allegato a questo articolo trovate, oltre al codice sorgente, il Code Snippet per generare le classi senza dover scrivere una riga di codice.
Per maggiori informazioni sulla tecnologia Code Snippet si rimanda all'articolo Introduzione alla tecnologia "Code Snippet".