Antonio Feliziani
a- a+

I Generics

Spesso utilizzando il namespace System.Collections con ilframework 1.0 o 1.1 abbiamo ricevuto a run-time questaeccezione :

“Unhandled Exception: System.InvalidCastException:Specified cast is not valid.”

Ricevere questo tipo di errore a run-time come tutti credosapete, è dato da un cast sbagliato che, nella maggiorparte dei casi, è legato all’utilizzo di unodegli oggetti di System.Collections.

Ogni elemento di una collection viene memorizzato comeObject, di conseguenza, per inserire o recuperare un itemè necessario effettuare l’unboxing o il boxingad uno specifico tipo di dato.

Questo meccanismo è causa, non solo, di numerosierrori, ma soprattutto introduce un forte overhead e, quindi,un decadimento delle prestazioni, che è ancorapiù percettibile quando usiamo una collection in unciclo.

Nel framework 2.0 questo problema è stato risolto conl’introduzione dei Generics.

I Generics sono stati introdotti con il framework 2.0 efanno parte del namespace System.Collections.Generic, sonoclassi che permettono di creare tipi di dati sicuri (TypeSafety) a tempo di compilazione, migliorano le prestazioni efacilitano il riutilizzo del codice.

I Generics hanno principalmente tre caratteristiche:

1. Sono Type Safety perché forzano il controllo sullaconformità del tipo (type compliance) a compile- timee non a run-time, come accadeva per System.Collections;

2. Aumentano le prestazioni perché non utilizzano ilcasting a run-time, poiché il tipo di dato dautilizzare viene stabilito a compile-time;

3. Favorisce il riutilizzo del codice perché sono tipidi dati parametrizzati e, quindi, è possibile usarliin più occasioni variando ogni volta il parametro.

In definitiva, con i Generics è possibile crearestrutture di dati senza specificare subito il tipo di datoche verrà utilizzato. Inoltre, il namespaceSystem.Collections.Generic mette a disposizione classigià pronte che implementano i tipi di strutture datipiù comuni :

  1. Collection, equivalente generico di CollectionBase.
  2. Dictionary, equivalente generico di HashTable.
  3. LinkedList, non ha equivalente non generico.
  4. List, equivalente generico di ArrayList.
  5. Queue (FIFO), stesso nome dell'equivalente non generico.
  6. ReadOnlyCollection, equivalente generico di ReadOnlyCollectionBase.
  7. SortedDictionary, non equivalente non generico.
  8. SortedList, stesso nome dell'equivalente non generico.
  9. Stack (LIFO), stesso nome dell'equivalente non generico.


Ad esempio portiamo un classico “Sort”

Prima di tutto creiamo un form.

E a questo punto possiamo andare a sperimentare in manierabase le potenzialità dei Generics.

Premetto che userò un solo file per una vostrapiù facile lettura .

Form1.vb


 

Imports System.CollectionsPublicClass Generics   Dim genericDataType As Type   Dim stringList As List(Of String)   Dim longList As List(Of LongClass)   Dim customerList As List(Of Customer)   Private Sub btnSort_Click(ByVal senderAs System.Object, ByVal e As System.EventArgs) Handles btnSort.Click  listTargetData.Items.Clear()  SortList()   End Sub   Private Sub SortList() Select Case genericDataType.NameCase "String"    ListSort(stringList)Case "LongClass"    ListSort(longList)Case "Customer"    ListSort(customerList) End Select  LoadList(False)   End Sub   Private Sub ListSort(Of ItemType)(ByVal list As List(Of ItemType))  list.Sort()   End Sub   Private Sub LoadList(Optional ByVal recreate As Boolean = True) If recreate ThenCreateList() End If Select Case genericDataType.NameCase "String"    ListDisplay(stringList)Case "LongClass"    ListDisplay(longList)Case "Customer"    ListDisplay(customerList) End Select   End Sub   Private Sub ListDisplay(Of ItemType)(ByVal list As List(Of ItemType)) For i AsInteger = 0 To list.Count - 1listTargetData.Items.Add(list(i))  Next   End Sub #Region"Assegno i valori"   Private Sub btnObject_Click(ByVal senderAs System.Object, ByVal e As System.EventArgs) Handles btnObject.Click  listSourceData.Items.Clear()  listTargetData.Items.Clear() Dim c As Customer  c = New Customer("Antonio Feliziani" , 1)  listSourceData.Items.Add(c)  c = New Customer("Lorenzo Pascucci" , 2)  listSourceData.Items.Add(c)  c = New Customer("Sara Bianchi" , 3)  listSourceData.Items.Add(c)  c = New Customer("Ivo Marzetti" , 4)  listSourceData.Items.Add(c)  c = New Customer("Daniele Rossi" , 5)  listSourceData.Items.Add(c)  genericDataType = GetType(Customer)  CreateList()   End Sub    Private Sub btnLong_Click(ByVal senderAs System.Object, ByVal e As System.EventArgs) Handles btnLong.Click  listSourceData.Items.Clear()  listTargetData.Items.Clear() Dim l As LongClass  l = New LongClass(6)  listSourceData.Items.Add(l)  l = New LongClass(447)  listSourceData.Items.Add(l)  l = New LongClass(780812)  listSourceData.Items.Add(l)  l = New LongClass(99)  listSourceData.Items.Add(l)  l = New LongClass(-1)  listSourceData.Items.Add(l)  genericDataType = GetType(LongClass)  CreateList()   End Sub    Private Sub btnString_Click(ByVal senderAs System.Object, ByVal e As System.EventArgs) Handles btnString.Click  listSourceData.Items.Clear()  listTargetData.Items.Clear()  listSourceData.Items.Add("Daniela Verdi")  listSourceData.Items.Add("Emauele Gistra")  listSourceData.Items.Add("Pino Menichelli")  listSourceData.Items.Add("Massimo Neri")  listSourceData.Items.Add("Sandro Castelli")  genericDataType = GetType(String)  CreateList()   End Sub#EndRegion    Private Sub CreateList() Select Case genericDataType.NameCase "String"    stringList = New System.Collections.Generic.List(OfString)    ListAdd(stringList)Case "LongClass"    longList = New System.Collections.Generic.List(Of LongClass)    ListAdd(longList)Case "Customer"    customerList = New System.Collections.Generic.List(Of Customer)    ListAdd(customerList) End Select   End Sub   Private Sub ListAdd(Of ItemType)(ByVal list As List(Of ItemType)) For i AsInteger = 0 To listSourceData.Items.Count - 1list.Add(CType(listSourceData.Items(i), ItemType)) Next   End SubEndClass #Region"Class Customer" PublicClass Customer   Implements IComparable   Private mName AsString   Private mId AsInteger   Public SubNew(ByVal new_name As String,ByVal new_id AsInteger)  mName = new_name  mId = new_id   End Sub   Public OverridesFunction ToString() AsString Return mId.ToString() + ": " + mName   End Function   Public Function CompareTo(ByVal obj AsObject) AsInteger Implements IComparable.CompareTo If TypeOf objIs Customer ThenDim c As Customerc = CType(obj, Customer)If c.ID = Me.ID Then    Return 0ElseIf c.ID <Me.ID Then    Return 1Else    Return -1End If ElseThrowNew ArgumentException("Un utente Istanza di ""Customer"" può essere comparato solo ad un'altro Customer.")  EndIf   End Function   Public Property Name() As String GetReturn mName End Get Set(ByVal ValueAs String)mName = Value End Set   End Property   Public Property ID() As Integer GetReturn mId End Get Set(ByVal ValueAs Integer)mId = Value End Set   End PropertyEndClass#EndRegion #Region"Class LongClass" PublicClass LongClass   Implements IComparable   Public SubNew(ByVal lAs Long)  v = l   End Sub   Private v AsLong   Public Property Value() As Long GetReturn v End Get Set(ByVal ValueAs Long)v = Value End Set   End Property   Public OverridesFunction ToString() AsString Return v.ToString()   End Function    Public Function CompareTo(ByVal obj AsObject) AsInteger Implements IComparable.CompareTo If TypeOf objIs LongClass ThenDim l As LongClassl = CType(obj, LongClass) If l.Value =Me.Value Then    Return 0ElseIf l.Value <Me.Value Then    Return 1Else    Return -1End If ElseThrowNew ArgumentException("Un Numero istanza di ""LongClass"" può essere comparato solo ad un'altro LongClass.")  EndIf   End FunctionEndClass#EndRegion

Grazie ai 'Generics' lavoriamo con il tipo usatonella dichiarazione quindi il casting non è piùnecessario e allo stesso tempo possiamo finalmente creare delcodice realmente riutilizzabile per un'infinità disituazioni senza correre il rischio di lasciare qualcosa dierrato in giro perchè il compilatore è in gradodi avvertirci quando qualcosa non va.