A37. Utilizzo delle Interfacce, Parte I
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:
Dopo aver immesso un input, ad esempio 8, avremo la seguente schermata:Module Module1Sub Main()Dim AAs 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) = 1Then Console.WriteLine(A & " è maggiore di 10")ElseIf A.CompareTo(10) = 0Then 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 IAs Int16 = 0To UBound(B) Console.WriteLine(B(I))Next 'Anche String espone questo metodo, quindi si può ordinare 'alfabeticamente un insieme di stringhe: Dim CAs New ArrayList C.Add("Banana") C.Add("Zanzara") C.Add("Anello") C.Add("Computer") 'Ordina l'insieme C.Sort()For IAs Int16 = 0To C.Count - 1 Console.WriteLine(C(I))Next Console.ReadKey()End Sub End Module
Inserire un numero intero: 8 8 è minore di 10 1 2 5 8 10 56 Anello Banana Computer ZanzaraCome 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:
Dato che il nome viene prima del congnome, la lista sarà: Antonio, Bianca, Guido, Marcello. Tuttavia, 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 implementa 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 Module1Class PersonImplements IComparablePrivate _FirstName, _LastNameAs String Private ReadOnly _BirthDayAs Date Public Property FirstName()As String Get Return _FirstNameEnd Get Set (ByVal ValueAs String )If Value <> ""Then _FirstName = ValueEnd If End Set End Property Public Property LastName()As String Get Return _LastNameEnd Get Set (ByVal ValueAs String )If Value <> ""Then _LastName = ValueEnd If End Set End Property Public ReadOnly Property BirthDay()As Date Get Return _BirthDayEnd Get End Property Public ReadOnly Property CompleteName()As String Get Return _FirstName & " " & _LastNameEnd 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 objAs Object )As Integer _Implements IComparable.CompareTo 'Un oggetto non-nothing (questo) è sempre maggiore di 'un oggetto Nothing (ossia obj) If objIs Nothing Then Return 1End If 'Tenta di convertire obj in Person Dim PAs 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 FirstNameAs String ,ByVal LastNameAs String , _ByVal BirthDayAs Date )Me .FirstName = FirstNameMe .LastName = LastNameMe ._BirthDay = BirthDayEnd 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 IAs Int16 = 0To 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.Module Module2 'Questa classe fornisce un metodo per comparare oggetti Person 'utilizzando la proprietà BirthDay 'Per convenzione, classi che utilizzano IComparer dovrebbero avere 'un suffisso "Comparer" nel nome Class BirthDayComparer 'Implementa l'interfaccia Implements IComparer 'Anche questa funzione deve usare parametri object Public Function Compare(ByVal xAs Object ,ByVal yAs Object ) _As IntegerImplements System.Collections.IComparer.Compare 'Se entrambi gli oggetti sono Nothing, allora sono uguali If xIs Nothing And yIs Nothing Then Return 0ElseIf xIs Nothing Then 'Se x è Nothing, y è maggiore Return -1ElseIf yIs Nothing Then 'Se y è Nothing, x è maggiore Return 1Else Dim P1As Person = DirectCast(x, Person)Dim P2As 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 IAs Int16 = 0To UBound(Persons) Console.WriteLine(Persons(I).CompleteName)Next Console.ReadKey()End Sub End Module
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.
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.



