A42. I Generics, Parte II
Interfacce Generics
Tutte le interfacce già analizzate presentano la stessa versione con i generic. Ad esempio, si può trovare IEnumerable(Of T) o IComparer(Of T).
Una cosa importante da notare è che nell'implementazione dell'interfaccia si deve specificare il tipo generic collegato:
Tutti i metodi esposti da IEnumerator faranno quindi riferimento al tipo Integer. Per questo motivo, se si vuole che la classe a tipizzazione forte possa essere usata per più tipi diversi, bisogna implementare più interfacce generic, con diverso tipo.Class NewEnumeratorImplements IEnumerator(Of Integer) '... End Class
Vincoli
Un vincolo è una clausola che specifica quale condizioni il tipo generic deve soddisfare affinchè possa essere collegato con un tipo qualsiasi.
Ad esempio, è possibile far sì che si accettino solo oggetti che implementino una certa interfaccia o che derivino da una data classe.
Infatti esistono molteplici tipi di vincoli:
- Vincolo di interfaccia
- Vincolo di erditarieta'
- Vincolo di classe
- Vincolo di struttura
- Vincolo New
Il vincolo di interfaccia impone che il tipo passato implementi una data interfaccia. In questo modo si è sicuri di poter richiamare i metodi e le proprietà di quell'interfaccia senza lanciare nessuna eccezione. La sintassi è la seguente:
[Classe/Metodo](Ad esempio, il codice che segue utilizza il vincolo di interfaccia per imporre il generic come IComparable e definisce una procedura che restituisce il minimo valore di un array di elementi:Of [Tipo]As [Interfaccia])
Il vincolo di ereditarietà impone che il tipo passato sia derivato da una data classe. In questo modo si è sicuri di poter rchiamarne i metodi e le proprietà senza che si verifichino errori di sorta. Fanno eccezione a questa regola Object, ValueType e Delegate, che non possono essere usati nei vincoli di erediarietà. La sintassi è simile a quella vista precedentemente:Module Module1 'Da notare che IComparable è comunque sempre a tipizzazione debole 'se si vuole che l'interfaccia sia specifica solo per il tipo 'dichiarato bisogna usare un ulteriore generic Function Min(Of TAs IComparable(Of T))(ByVal ParamArray Valori() _As T)As TDim TempAs T = Valori(0)For IAs Int32 = 1To UBound(Valori)If Valori(I).CompareTo(Temp) = -1Then Temp = Valori(I)End If Next Return TempEnd Function Sub Main() Console.WriteLine(Min(2, 8, 10, 80, 45).ToString) '> 2 'In casi come questo, però, bisogna specificare il tipo 'come già si è visto nella lezione precedente Console.WriteLine(Min(Of Single )(1.0, 9, 20, 39)) '> 1 Console.ReadKey()End Sub End Module
[Classe/Metodo](La classe che segue utilizza il vincolo suddetto in modo da definire alcune utili proprietà di una collezione a tipizzazione relativamente forte:Of [Tipo]As [Classe])
I vincoli di classe e di struttura sono simili, poichè specificano che il tipo passato deve essere o reference oppure value. Anche se apparentemente potrebbe non apparire chiaro il loro scopo, non sono così inutili come sembra. Infatti ci sono certi operatori del vb.net che possono essere utilizzati solo con i tipi reference, quali Is, IsNot, TypeOf, DirectCast e TryCast.Module Module2 'Questa classe può raccogliere qualsiasi tipo di oggetto derivato da 'person. Per questo motivo, si possono dichiarare con sicurezza le 'proprietà readonly CompleteName, FirstName, LastName e BirthDay 'che restituiscono i rispettivi dati dell'Index-esimo elemento della 'lista. Oltre ad essere molto comode, il vincolo di ereditarietà 'permette di richiamare tali proprietà senza errori Class StrongPersonCollection(Of TAs Person) 'Eredita da List generic, implementando in questo modo 'tutti i meotodi e le proprietà di default delle liste Inherits List(Of T)Public ReadOnly Property CompleteName(ByVal IndexAs Int32)As _String Get If Index >= 0And Index <Me .CountThen Return Me .Item(Index).CompleteNameElse Return "N.N."End If End Get End Property Public ReadOnly Property FirstName(ByVal IndexAs Int32)As String Get If Index >= 0And Index <Me .CountThen Return Me .Item(Index).FirstNameElse Return "N.N."End If End Get End Property Public ReadOnly Property LastName(ByVal IndexAs Int32)As String Get If Index >= 0And Index <Me .CountThen Return Me .Item(Index).LastNameElse Return "N.N."End If End Get End Property Public ReadOnly Property BirthDay(ByVal IndexAs Int32)As Date Get If Index >= 0And Index <Me .CountThen Return Me .Item(Index).BirthDayElse Return NothingEnd If End Get End Property End Class Sub Main()Dim SAs New StrongPersonCollection(Of Student) S.Add(New Student("Pinco", "Pallino",Date .Parse("01/01/90"), _ "Liceo", 4)) Console.WriteLine(S.CompleteName(0)) Console.WriteLine(S.BirthDay(0).ToShortDateString) Console.ReadKey()End Sub End Module
'L'ultimo vincolo, invece, specifica che il tipo passato deve sempre avere almeno un costruttore senza parametri. In questo modo è possibile instanziare oggetti anche tramite metodi generic. Ad esempio, il seguente codice sfrutta il vincolo new per creare un array di oggetti di qualsiasi tipo:Vincolo di struttura [Classe/Metodo](Of [Tipo]As Structure) 'Vincolo di classe [Classe/Metodo](Of [Tipo]As Class)
Module Module3Function CreateArray(Of TAs New )(ByVal CountAs Int32)As T()Dim Ar(Count - 1)As TFor IAs Int32 = 0To UBound(Ar) Ar(I) =New TNext Return ArEnd Function Sub Main() 'Per quanto possa sembrare strano, Byte ha un 'costruttore senza parametri Dim Bytes()As Byte = CreateArray(Of Byte )(10) Console.ReadKey()End Sub End Module
Vincoli multipli
I vincoli multipli permettono di specificare più vincoli per un solo tipo generic. Ogni vincolo deve essere separato dagli altri con una
virgola (come se fossero dei parametri di funzione) e tutti devono essere racchiusi da una parentesi graffa. Ad esempio, questo codice
usa un vincolo di classe e uno di interfaccia per creare un array di elementi con un valore di default preimpostato:
Anche se si possono soddisfare molte esigenze, i vincoli multipli non supportano ogni tipo di comportamento. Infatti non è possibile fare in modo che il tipo possa implementare un'interfaccia o un'altra o entrambe, o che non derivi da una data classe, o che non abbia un costruttore senza parametri. Per questo motivo, bisogna eseguire dei controlli manuali in casi simli e l'unico modo per fare ciò è utilizzare la reflection. In questo esempio, accenno brevemente a come si possa verificare che un tipo generic implementi una delle interfacce volute:Module Module4Function CreateArray(Of TAs {Class , ICloneable})(ByVal CountAs Int32, _ByVal DefaultValueAs T)As T()Dim Ar(Count - 1)As TFor IAs Int32 = 0To UBound(Ar) Ar(I) = DefaultValue.CloneNext Return ArEnd Function Sub Main() 'Crea un array di stringhe tutte con lo stesso valore, "Ciao", ma 'ognuna distinta dalle altre in quanto generata dal metodo Clone Dim Strings()As String = CreateArray(10, "Ciao") Console.ReadKey()End Sub End Module
String implementa IComparable, ArrayList entrambe, e UnOggetto nessuna: infatti il messaggio che appare a schermo lo comunica.Module Module5Class UnaDelleDue(Of T)Sub New () 'Controlla tramite reflection che IComparable o 'IEnumerable siano implementati da T If Not (GetType (IComparable).IsAssignableFrom(GetType (T))Or _GetType (IEnumerable).IsAssignableFrom(GetType (T)))Then Console.WriteLine(GetType(T).ToString & _ " non implementa nessuna delle interfacce richieste!")End If End Sub End Class Sub Main()Dim AAs New UnaDelleDue(Of String )Dim BAs New UnaDelleDue(Of ArrayList) 'La classe UnOggetto è stata definita nella lezione 'sull'interfaccia ICloneable Dim CAs New UnaDelleDue(Of UnOggetto) '> App_Prove_Console.Module1+UnOggetto non implementa 'nessuna delle interfacce richieste! Console.ReadKey()End Sub End Module
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.



