|
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.
|
|