Casino online











Mercato forex






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:
Class NewEnumerator
  Implements IEnumerator(Of Integer)
  '...
End Class 
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.


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
Veniamo ora all'analisi di ogni vincolo.
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](Of [Tipo] As [Interfaccia]) 
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:
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 T As IComparable(Of T))(ByVal ParamArray Valori() _ 
        As T) As T
        Dim Temp As T = Valori(0)
        For I As Int32 = 1 To UBound(Valori)
            If Valori(I).CompareTo(Temp) = -1 Then
                Temp = Valori(I)
            End If
        Next
        Return Temp
    End 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 
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:
[Classe/Metodo](Of [Tipo] As [Classe]) 
La classe che segue utilizza il vincolo suddetto in modo da definire alcune utili proprietà di una collezione a tipizzazione relativamente forte:
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 T As 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 Index As Int32) As _ 
            String
            Get
                If Index >= 0 And Index < Me.Count Then
                    Return Me.Item(Index).CompleteName
                Else
                    Return "N.N."
                End If
            End Get
        End Property

        Public ReadOnly Property FirstName(ByVal Index As Int32) As String
            Get
                If Index >= 0 And Index < Me.Count Then
                    Return Me.Item(Index).FirstName
                Else
                    Return "N.N."
                End If
            End Get
        End Property

        Public ReadOnly Property LastName(ByVal Index As Int32) As String
            Get
                If Index >= 0 And Index < Me.Count Then
                    Return Me.Item(Index).LastName
                Else
                    Return "N.N."
                End If
            End Get
        End Property

        Public ReadOnly Property BirthDay(ByVal Index As Int32) As Date
            Get
                If Index >= 0 And Index < Me.Count Then
                    Return Me.Item(Index).BirthDay
                Else
                    Return Nothing
                End If
            End Get
        End Property
    End Class

    Sub Main()
        Dim S As 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 
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.
'Vincolo di struttura
[Classe/Metodo](Of [Tipo] As Structure)
'Vincolo di classe
[Classe/Metodo](Of [Tipo] As Class) 
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:
Module Module3
    Function CreateArray(Of T As New)(ByVal Count As Int32) As T()
        Dim Ar(Count - 1) As T
        For I As Int32 = 0 To UBound(Ar)
            Ar(I) = New T
        Next
        Return Ar
    End 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:
Module Module4
    Function CreateArray(Of T As {Class, ICloneable})(ByVal Count As Int32, _
        ByVal DefaultValue As T) As T()
        Dim Ar(Count - 1) As T
        For I As Int32 = 0 To UBound(Ar)
            Ar(I) = DefaultValue.Clone
        Next
        Return Ar
    End 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 
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 Module5
    Class 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 A As New UnaDelleDue(Of String)
        Dim B As New UnaDelleDue(Of ArrayList)
        'La classe UnOggetto è stata definita nella lezione
        'sull'interfaccia ICloneable
        Dim C As New UnaDelleDue(Of UnOggetto)
        '> App_Prove_Console.Module1+UnOggetto non implementa 
        'nessuna delle interfacce richieste!

        Console.ReadKey()
    End Sub
End Module 
String implementa IComparable, ArrayList entrambe, e UnOggetto nessuna: infatti il messaggio che appare a schermo lo comunica.




 

The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.