Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Il Polimorfismo

Guida al Visual Basic .NET

Capitolo 30° - Il Polimorfismo

<< Precedente Prossimo >>


Il polimorfismo è la capacità di un linguaggio ad oggetti di ridefinire i membri della classe base in modo tale che si comportino in maniera differente all'interno delle classi derivate. Questa possibilità è quindi strettamente legata all'ereditarietà. Le keywords che permettono di attuarne il funzionamento sono due: Overridable e Overrides. La prima deve marcare il membro della classe base che si dovrà ridefinire, mentre la seconda contrassegna il membro della classe derivata che ne costituisce la nuova versione. È da notare che solo membri della stessa categoria con nome uguale e signature identica (ossia con lo stesso numero e lo stesso tipo di parametri) possono subire questo processo: ad esempio non si può ridefinire la procedura ShowText() con la proprietà Text, perchè hanno nome differente e sono di diversa categoria (una è una procedura e l'altra una proprietà). La sintassi è semplice:
Class [Classe base]
    Overridable [Membro]
End Class

Class [Classe derivata]
    Inherits [Classe base]
    Overrides [Membro]
End Class  
Questo esempio prende come base la classe Person definita nel capitolo precedente e sviluppa da questa la classe Teacher (insegnante), modificandone le proprietà LastName e CompleteName:
Module Module1
    Class Person
        Protected _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

        'Questa proprietà sarà ridefinita nella classe Teacher 
        Public Overridable 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

        'Questa proprietà sarà ridefinita nella classe Teacher
        Public Overridable ReadOnly Property CompleteName() As String
            Get
                Return _FirstName & " " & _LastName
            End Get
        End Property

        'Costruttore che accetta tra parametri obbligatori
        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

    Class Teacher
        Inherits Person
        Private _Subject As String

        Public Property Subject() As String
            Get
                Return _Subject
            End Get
            Set(ByVal Value As String)
                If Value <> "" Then
                    _Subject = Value
                End If
            End Set
        End Property

        'Ridefinisce la proprietà LastName in modo da aggiungere
        'anche il titolo di Professore al cognome
        Public Overrides Property LastName() As String
            Get
                Return "Prof. " & _LastName
            End Get
            Set(ByVal Value As String)
                'Da notare l'uso di MyBase e LastName: in questo
                'modo si richiama la vecchia versione della
                'proprietà LastName e se ne imposta il
                'valore. Viene quindi richiamato il blocco Set
                'vecchio: si risparmiano due righe di codice
                'poichè non si deve eseguire il controllo
                'If su Value
                MyBase.LastName = Value
            End Set
        End Property

        'Ridefinisce la proprietà CompleteName in modo da
        'aggiungere anche la materia insegnata e il titolo di
        'Professore
        Public Overrides ReadOnly Property CompleteName() As String
            Get
                'Anche qui viene richiamata la vecchia versione di
                'CompleteName, che restituisce semplicemente il
                'nome completo
                Return "Prof. " & MyBase.CompleteName & _
                ", dottore in " & Subject
            End Get
        End Property

        Sub New(ByVal FirstName As String, ByVal LastName As String, _
            ByVal BirthDay As Date, ByVal Subject As String)
            MyBase.New(FirstName, LastName, BirthDay)
            Me.Subject = Subject
        End Sub
    End Class

    Sub Main()
        Dim T As New Teacher("Mario", "Rossi", Date.Parse("01/01/1950"), _
        "Letteratura italiana")

        'Usiamo le nuove proprietà, ridefinite nella classe
        'derivata
        Console.WriteLine(T.LastName)
        '> "Prof. Rossi"
        Console.WriteLine(T.CompleteName)
        '> "Prof. Mario Rossi, dottore in Letteratura italiana"

        Console.ReadKey()
    End Sub
End Module  
In questo modo si è visto come ridefinire le proprietà. Ma prima di proseguire vorrei far notare un comportamento particolare:
Dim P As Person = T
Console.WriteLine(P.LastName)
Console.WriteLine(P.CompleteName) 
In questo caso ci si aspetterebbe che le proprietà richiamate da P agiscano come specificato nella classe base (ossia senza includere altre informazioni se non il nome ed il cognome), poiché P è di quel tipo. Questo, invece, non accade. Infatti, P e T, dato che abbiamo usato l'operatore =, puntano ora allo stesso oggetto in memoria, solo che P lo vede come di tipo Person e T come di tipo Teacher. Tuttavia, l'oggetto reale è di tipo Teacher e perciò i suoi metodi sono a tutti gli effetti quelli ridefiniti nella classe derivata. Quando P tenta di richiamare le proprietà in questione, arriva all'indirizzo di memoria dove sono conservate le istruzioni da eseguire, solo che queste si trovano all'interno di un oggetto Teacher e il loro codice è, di conseguenza, diverso da quello della classe base. Questo comportamento, al contrario di quanto potrebbe sembrare, è utilissimo: ci permette, ad esempio, di memorizzare in un array di persone sia studenti che insegnanti, e ci permette di scrivere a schermo i loro nomi differentemente senza eseguire una conversione. Ecco un esempio:
Dim Ps(2) As Person

Ps(0) = New Person("Luigi", "Ciferri", Date.Parse("7/7/1982"))
Ps(1) = New Student("Mario", "Bianchi", Date.Parse("19/10/1991"), _
    "Liceo Scientifico Tecnologico Cardano", 5)
Ps(2) = New Teacher("Ubaldo", "Nicola", Date.Parse("11/2/1980"), "Filosofia")

For Each P As Person In Ps
    Console.WriteLine(P.CompleteName)
Next 
È lecito assegnare oggetti Student e Teacher a una cella di un array di Person in quanto classi derivate da Person. I metodi ridefiniti, tuttavia, rimangono e modificano il comportamento di ogni oggetto anche se richiamato da una "maschera" di classe base. Proviamo ora con un piccolo esempio sul polimorfismo dei metodi:
Class A
    Public Overridable Sub ShowText()
        Console.WriteLine("A: Testo di prova")
    End Sub
End Class

Class B
    Inherits A

    'Come si vede il metodo ha:
    '- lo stesso nome: ShowText
    '- lo stesso tipo: è una procedura
    '- gli stessi parametri: senza parametri
    'Qualunque tentativo di cambiare una di queste caratteristiche
    'produrrà un errore del compilatore, che comunica di non poter
    'ridefinire il metodo perchè non ne esistono di uguali nella
    'classe base
    Public Overrides Sub ShowText()
        Console.WriteLine("B: Testo di prova")
    End Sub
End Class  
Ultime due precisazioni: le variabili non possono subire polimorfismo, così come i membri statici.


Shadowing

Se il polimorfismo permette di ridefinire accuratamente membri che presentano le stesse caratteristiche, ed è quindi più preciso, lo shadowing permette letteralmente di oscurare qualsiasi membro che abbia lo stesso nome, indipendentemente dalla categoria, dalla signature e dalla qauntità di versioni alternative presenti. La keyword da usare è Shadows, e si applica solo sul membro della classe derivata che intendiamo ridefinire, oscurando l'omonimo nella classe base. Ad esempio:
Module Esempio
    Class Base
        Friend Control As Byte
    End Class

    Class Deriv
        Inherits Base
        Public Shadows Sub Control(ByVal Msg As String)
            Console.WriteLine("Control, seconda versione: " & Msg)
        End Sub
    End Class

    Sub Main()
        Dim B As New Base
        Dim D As New Deriv

        'Entrambe le classe hanno lo stesso membro di nome
        '"Control", ma nella prima è un campo friend,
        'mentre nella seconda è una procedura pubblica
        Console.WriteLine(B.Control)
        D.Control("Ciao")

        Console.ReadKey()
    End Sub
End Module  
Come si vede, la sintassi è come quella di Overrides: Shadows viene specificato tra lo specificatore di accesso (se c'e') e la tipologia del membro (in questo caso Sub, procedura). Entrambe le classi presentano Control, ma la seconda ne fa un uso totalmente diverso. Ad ogni modo l'uso dello shadowing in casi come questo è fortememente sconsigliabile: più che altro lo si usa per assicurarsi che, se mai dovesse uscire una nuova versione della classe base con dei nuovi metodi che presentano lo stesso nome di quelli della classe derivata da noi definita, non ci siano problemi di compatibilità.
Se una variabile è dichiarata Shadows, viene omessa la keyword Dim.


<< Precedente Prossimo >>
A proposito dell'autore

Programmatore e analista .NET 2005/2008/2010 (in particolare C# e VB.NET), anche nell'implementazione Mono per Linux. Conoscenze approfondite di Pascal, PHP, XML, HTML 4.01/5, CSS 2.1/3, Javascript (e jQuery). Conoscenze buone di C, LUA, GML, Ruby, XNA, AJAX e Assembly 68000. Competenze basilari di C++, SQL, Hlsl, Java.