Guida al Visual Basic .NET
Capitolo 30° - Il Polimorfismo
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 ClassQuesto 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 ModuleIn 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 ClassUltime due precisazioni: le variabili non possono subire polimorfismo, così come i membri statici. ShadowingSe 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 ModuleCome 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.
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|