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 ModuleDopo 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 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:
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 ModuleDato 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 ModuleUsando 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