Oppure

Loading


L'aspetto più interessante e sicuramente più utile delle interfacce è che il loro utilizzo è fondamentale per l'uso di alcuni costrutti particolari, quali il For Each e l'Using, e per molte altre funzioni e procedure che intervengono nella gestione delle collezioni. Imparare a manipolare con facilità questo strumento permetterà di scrivere non solo meno codice, più efficace e riusabile, ma anche di impostare l'applicazione in una maniera solida e robusta.


IComparable e IComparer

Un oggetto che implementa IComparable comunica implicitamente al .NET Framework che può essere confrontato con altri oggetti, stabilendo se uno di essi è maggiore, minore o uguale all'altro e abilitando in questo modo l'ordinamento automatico attraverso il metodo Sort di una collection. Infatti, tale metodo confronta uno ad uno ogni elemento di una collezione o di un array e tramite la funzione CompareTo che ogni interfaccia IComparable espone e li ordina in ordine crescente o decrescente. CompareTo è una funzione di istanza che implementa IComparable.CompareTo e ha dei risultati predefiniti: restituisce 1 se l'oggetto passato come parametro è minore dell'oggetto dalla quale viene richiamata, 0 se è uguale e -1 se è maggiore. Ad esempio, questo semplice programma illustra il funzionamento di CompareTo e Sort:
Module Module1
    Sub Main()
        Dim A As Int32

        Console.WriteLine("Inserisci un numero intero:")
        A = Console.ReadLine

        'Tutti i tipi di base espongono il metodo CompareTo, poichè
        'tutti implementano l'interfaccia IComparable:
        If A.CompareTo(10) = 1 Then
            Console.WriteLine(A & " è maggiore di 10")
        ElseIf A.CompareTo(10) = 0 Then
            Console.WriteLine(A & " è uguale a 10")
        Else
            Console.WriteLine(A & " è minore di 10")
        End If

        'Il fatto che i tipi di base siano confrontabili implica
        'che si possano ordinare tramite il metodo Sort di una
        'qualsiasi collezione o array di elementi
        Dim B() As Int32 = {1, 5, 2, 8, 10, 56}
        'Ordina l'array
        Array.Sort(B)
        'E visualizza i numeri in ordine crescente
        For I As Int16 = 0 To UBound(B)
            Console.WriteLine(B(I))
        Next

        'Anche String espone questo metodo, quindi si può ordinare
        'alfabeticamente un insieme di stringhe:
        Dim C As New ArrayList
        C.Add("Banana")
        C.Add("Zanzara")
        C.Add("Anello")
        C.Add("Computer")
        'Ordina l'insieme
        C.Sort()
        For I As Int16 = 0 To C.Count - 1
            Console.WriteLine(C(I))
        Next

        Console.ReadKey()
    End Sub
End Module 
Dopo aver immesso un input, ad esempio 8, avremo la seguente schermata:
Inserire un numero intero:
8
8 è minore di 10
1
2
5
8
10
56
Anello
Banana
Computer
Zanzara 
Come si osserva, tutti gli elementi sono stati ordinati correttamente. Ora che abbiamo visto la potenza di IComparable, vediamo di capire come implementarla. L'esempio che prenderò come riferimento ora pone una semplice classe Person, di cui si è già parlato addietro, e ordina un ArrayList di questi oggetti prendendo come riferimento il nome completo:
Module Module1
    Class Person
        Implements IComparable
        Private _FirstName, _LastName As String
        Private ReadOnly _BirthDay As Date

        Public Property FirstName() As String
            Get
                Return _FirstName
            End Get
            Set(ByVal Value As String)
                If Value <> "" Then
                    _FirstName = Value
                End If
            End Set
        End Property

        Public Property LastName() As String
            Get
                Return _LastName
            End Get
            Set(ByVal Value As String)
                If Value <> "" Then
                    _LastName = Value
                End If
            End Set
        End Property

        Public ReadOnly Property BirthDay() As Date
            Get
                Return _BirthDay
            End Get
        End Property

        Public ReadOnly Property CompleteName() As String
            Get
                Return _FirstName & " " & _LastName
            End Get
        End Property

        'Per definizione, purtroppo, CompareTo deve sempre usare 
        'un parametro di tipo Object: risolveremo questo problema 
        'più in là utilizzando i Generics
        Public Function CompareTo(ByVal obj As Object) As Integer _ 
            Implements IComparable.CompareTo
            'Un oggetto non-nothing (questo) è sempre maggiore di 
            'un oggetto Nothing (ossia obj)
            If obj Is Nothing Then
                Return 1
            End If
            'Tenta di convertire obj in Person
            Dim P As Person = DirectCast(obj, Person)
            'E restituisce il risultato dell'operazione di
            'comparazione tra stringhe dei rispettivi nomi
            Return String.Compare(Me.CompleteName, P.CompleteName)
        End Function

        Sub New(ByVal FirstName As String, ByVal LastName As String, _ 
            ByVal BirthDay As Date)
            Me.FirstName = FirstName
            Me.LastName = LastName
            Me._BirthDay = BirthDay
        End Sub
    End Class
    
    Sub Main()
        'Crea un array di oggetti Person
        Dim Persons() As Person = _
        {New Person("Marcello", "Rossi", Date.Parse("10/10/1992")), _
        New Person("Guido", "Bianchi", Date.Parse("01/12/1980")), _
        New Person("Bianca", "Brega", Date.Parse("23/06/1960")), _
        New Person("Antonio", "Felice", Date.Parse("16/01/1930"))}

        'E li ordina, avvalendosi di IComparable.CompareTo
        Array.Sort(Persons)

        For I As Int16 = 0 To UBound(Persons)
            Console.WriteLine(Persons(I).CompleteName)
        Next

        Console.ReadKey()
    End Sub
End Module 
Dato che il nome viene prima del congnome, la lista sarà: Antonio, Bianca, Guido, Marcello.
E se si volesse ordinare la lista di persone in base alla data di nascita? Non è possibile definire due versioni di CompareTo, poichè devono avere la stessa signature, e creare due metodi che ordinino l'array sarebbe scomodo: è qui che entra in gioco l'interfaccia IComparer. Essa rappresenta un oggetto che deve eseguire la comparazione tra due altri oggetti, facendo quindi da tramite nell'ordinamento. Dato che Sort accetta in una delle sue versioni un oggetto IComparer, è possibile ordinare una lista di elementi con qualsiasi criterio si voglia semplicemente cambiando il parametro. Ad esempio, in questo sorgente scrivo una classe BirthDayComparer che permette di ordinare oggetti Person in base all'anno di nascita:
Module Module2
    'Questa classe fornisce un metodo per comparare oggetti Person
    'utilizzando la proprietà BirthDay.
    'Per convenzione, classi che implementano IComparer dovrebbero
    'avere un suffisso "Comparer" nel nome.
    'Altra osservazione: se ci sono molte interfacce il cui nome
    'termina in "-able", definendo una caratteristica dell'oggetto
    'che le implementa (ad es.: un oggetto enumerabile,
    'comparabile, distruggibile, ecc...), ce ne sono altrettante
    'che terminano in "-er", indicando, invece, un oggetto 
    'che "fa" qualcosa di specifico.
    'Nel nostro esempio, oggetti di tipo BirthDayComparer
    'hanno il solo scopo di comparare altre oggetti
    Class BirthDayComparer
        'Implementa l'interfaccia
        Implements IComparer

        'Anche questa funzione deve usare parametri object
        Public Function Compare(ByVal x As Object, ByVal y As Object) _
            As Integer Implements System.Collections.IComparer.Compare
            'Se entrambi gli oggetti sono Nothing, allora sono
            'uguali
            If x Is Nothing And y Is Nothing Then
                Return 0
            ElseIf x Is Nothing Then
                'Se x è Nothing, y è maggiore
                Return -1
            ElseIf y Is Nothing Then
                'Se y è Nothing, x è maggiore
                Return 1
            Else
                Dim P1 As Person = DirectCast(x, Person)
                Dim P2 As Person = DirectCast(y, Person)
                'Compara le date
                Return Date.Compare(P1.BirthDay, P2.BirthDay)
            End If
        End Function
    End Class

    Sub Main()
        Dim Persons() As Person = _
        {New Person("Marcello", "Rossi", Date.Parse("10/10/1992")), _
        New Person("Guido", "Bianchi", Date.Parse("01/12/1980")), _
        New Person("Bianca", "Brega", Date.Parse("23/06/1960")), _
        New Person("Antonio", "Felice", Date.Parse("16/01/1930"))}

        'Ordina gli elementi utilizzando il nuovo oggetto
        'inizializato in linea BirthDayComparer
        Array.Sort(Persons, New BirthDayComparer())

        For I As Int16 = 0 To UBound(Persons)
            Console.WriteLine(Persons(I).CompleteName)
        Next

        Console.ReadKey()
    End Sub
End Module 
Usando questo meccanismo è possibile ordinare qualsiasi tipo di lista o collezione fin'ora analizzata (tranne SortedList, che si ordina automaticamente), in modo semplice e veloce, particolarmente utile nell'ambito delle liste visuali a colonne, come vedremo nei capitoli sulle ListView.


IDisposable

Nel capitolo sui distruttori si è visto come sia possibile utilizzare il costrutto Using per gestire un oggetto e poi distruggerlo in poche righe di codice. Ogni classe che espone il metodo Dispose deve obbligatoriamente implementare anche l'interfaccia IDisposable, la quale comunica implicitamente che essa ha questa caratteristica. Dato che già molti esempi sono stati fatti sull'argomento distruttori, eviterò di trattare nuovamente Dispose in questo capitolo.

A cura di: Il Totem