A29. Ereditarietà
L'ereditarietà è la possibilità di un linguaggio ad oggetti di far derivare una classe da un'altra: in questo caso, la classe derivata
acquisisce tutti i membri della classe base, ma può ridefinirli (se vuole) o aggiungerne di nuovi. Questa caratteristica di ogni
linguaggio Object Oriented è particolarmente efficace nello schematizzare una relazione "is-a" (ossia "è un"). Per esempio, potremmo
definire una classe Vegetale, quindi una nuova classe Fiore, che eredita Vegetale. Fiore è un Vegetale, come mostra la struttura gerarchica
dell'ereditarietà. Se definissimo un'altra classe Primula, derivata da Fiore, diremmo che Primula è un Fiore, che a sua volta è un
Vegetale. Quest'ultimo tipo di relazione, che crea classi derivate che saranno basi per ereditare altre classi, si chiama ereditarietà
indiretta.
Passiamo ora a vedere come si dichiara una classe derivata:
Anche se il sorgente è ampiamente commentato mi soffermerei su alcuni punti caldi. Il costruttore della classe derivata deve sempre richiamare il costruttore della classe base, e questo avviene tramite la keyword MyBase che, usata in una classe derivata, fa riferimento alla classe base corrente: attraverso questa parola riservata è possibile anche raggiungere i membri privati della classe base, ma si fa raramente, poichè il suo impiego più frequente è quello di riprendere le vecchie versioni di metodi modificati. Il secondo punto riguarda la conversione di classi: passare da Student a Person non è, come potrebbe sembrare, una conversione di riduzione, poichè durante il processo, nulla va perduto nel vero senso della parola. Certo, si perdono le informazioni supplementari, ma alla classe base queste non servono: la sicurezza di eseguire la conversione risiede nel fatto che la classe derivata gode degli stessi membri di quella base e quindi non si corre il rischio che ci sia riferimento a un membro inesistente. Questo invece si verifica nel caso opposto: se una variabile di tipo Student assumesse il valore di un oggetto Person, School e Year sarebbero privi di valore e ciò generebbe un errore. Per eseguire questo tipo di passaggi è necessario l'operatore DirectCast.
Passiamo ora a vedere come si dichiara una classe derivata:
La keyword Inherits specifica quale classe base ereditare: si può avere solo una direttiva Inherits per classe, ossia non è possibile ereditare più classi base. In questo frangente, si può scoprire come le proprietà siano utili e flessibili: se una classe base definisce una variabile pubblica, questa diverrà parte anche della classe derivata e su tale variabile verranno basate tutte le operazioni che la coinvolgono. Siccome è possibile che la classe derivata voglia ridefinire tali operazioni e molto probabilmente anche l'utilizzo della variabile, è sempre consigliabile dichiarare campi Private avvolti da una proprietà, poichè non c'è mai alcun pericolo nel modificare una proprietà in classi derivate, ma non è possibile modificare i campi nella stessa classe. Un semplice esempio di ereditarietà:Class [Nome]Inherits [Classe base] 'Membri della classe End Class
In seguito, si può utilizzare la classe derivata come si è sempre fatto, con ogni altra classe: bisogna notare che questa può richiamare anche i membri della classe base, ma solo in relazione allo specificatore di accesso. Infatti, esistono delle regole nell'ereditarietà, che vengono applicate nell'azione di ereditare per cambiare l'ambito di visibilità di un membro.Class Person 'Per velocizzare la scrittura del codice, assumiamo che 'questi campi pubblici siano proprietà Public FirstName, LastNameAs String Public ReadOnly Property CompleteName()As String Get Return FirstName & " " & LastNameEnd Get End Property End Class 'Lo studente, ovviamente, è una persona Class Student 'Student eredita da Person Inherits Person 'In più, definisce anche questi campi pubblici 'La scuola frequentata Public SchoolAs String 'E l'anno di corso Public YearAs Byte End Class
- Un membro Public o Friend della classe base diventa un membro Public o Friend della classe derivata
- Un membro Private della classe base non è accessibile dalla classe derivata, poichè il suo ambito di visibilità impedisce a ogni chiamante esterno alla classe base di farvi riferimento, come già visto nelle lezioni precedenti
- Un membro Protected della classe base diventa un membro Protected della classe derivata, ma si comporta come un membro Private
L'output:Module EsempioClass Person 'Due campi protected Protected _FirstName, _LastNameAs String 'Un campo private readonly: non c'è ragione di rendere 'questo campo Protected poichè la data di nascita non 'cambia ed è sempre accessibile tramite la proprietà 'pubblica BirthDay Private ReadOnly _BirthDayAs Date Public Property FirstName()As String Get Return _FirstNameEnd Get Set (ByVal ValueAs String )If Value <> ""Then _FirstName = ValueEnd If End Set End Property Public Property LastName()As String Get Return _LastNameEnd Get Set (ByVal ValueAs String )If Value <> ""Then _LastName = ValueEnd If End Set End Property Public ReadOnly Property BirthDay()As Date Get Return _BirthDayEnd Get End Property Public ReadOnly Property CompleteName()As String Get Return _FirstName & " " & _LastNameEnd Get End Property 'Costruttore che accetta tra parametri obbligatori Sub New (ByVal FirstNameAs String ,ByVal LastNameAs String , _ByVal BirthDayAs Date )Me .FirstName = FirstNameMe .LastName = LastNameMe ._BirthDay = BirthDayEnd Sub End Class 'Lo studente, ovviamente, è una persona Class Student 'Student eredita da Person Inherits Person 'La scuola frequentata Private _SchoolAs String 'E l'anno di corso Private _YearAs Byte Public Property School()As String Get Return _SchoolEnd Get Set (ByVal ValueAs String )If Value <> ""Then _School = ValueEnd If End Set End Property Public Property Year()As Byte Get Return _YearEnd Get Set (ByVal ValueAs Byte )If Value > 0Then _Year = ValueEnd If End Set End Property 'Questa nuova proprietà si serve anche dei campi FirstName 'e LastName nel modo corretto, poichè sono Protected anche nella 'classe derivata e fornisce un profilo completo dello studente Public ReadOnly Property Profile()As String Get 'Da notare l'accesso a BirthDay tramite la proprietà 'Public: non è possibile accedere al campo _BirthDay 'perchè è privato nella classe base Return _FirstName & " " & _LastName & ", nato il " & _ BirthDay.ToShortDateString & " frequenta l'anno " & _ _Year & " alla scuola " & _SchoolEnd Get End Property 'Altra clausola importante: il costruttore della classe derivata 'deve sempre richiamare il costruttore della classe base Sub New (ByVal FirstNameAs String ,ByVal LastNameAs String , _ByVal BirthDayAs Date ,ByVal SchoolAs String , _ByVal YearAs Byte )My Base.New(FirstName, LastName, BirthDay)Me .School = SchoolMe .Year = YearEnd Sub End Class Sub Main()Dim PAs New Person("Pinco", "Pallino",Date .Parse("06/07/90"))Dim SAs New Student("Tizio", "Caio",Date .Parse("23/05/92"), _ "Liceo Classico Ugo Foscolo", 2) Console.WriteLine(P.CompleteName) 'Come si vede, la classe derivata gode degli stessi membri 'di quella base, acquisiti secondo le regole dell'ereditarietà 'appena spiegate Console.WriteLine(S.CompleteName) 'E in più ha anche i suoi nuovi membri Console.WriteLine(S.Profile) 'Altra cosa interessante: dato che Student è derivata da 'Person ed espone tutti i membri di Person, più altri, non è 'sbagliato assegnare un oggetto Student a una variabile Person P = S Console.WriteLine(P.CompleteName) Console.ReadKey()End Sub End Module
Pinco Pallino Tizio Caio Tizio Caio, nato il 23/5/1992 frequenta l'anno 2 alla scuola Liceo Classico Ugo Foscolo Tizio Caio(Per maggiori informazioni sulle operazioni con le date, vedere il capitolo 57)
Anche se il sorgente è ampiamente commentato mi soffermerei su alcuni punti caldi. Il costruttore della classe derivata deve sempre richiamare il costruttore della classe base, e questo avviene tramite la keyword MyBase che, usata in una classe derivata, fa riferimento alla classe base corrente: attraverso questa parola riservata è possibile anche raggiungere i membri privati della classe base, ma si fa raramente, poichè il suo impiego più frequente è quello di riprendere le vecchie versioni di metodi modificati. Il secondo punto riguarda la conversione di classi: passare da Student a Person non è, come potrebbe sembrare, una conversione di riduzione, poichè durante il processo, nulla va perduto nel vero senso della parola. Certo, si perdono le informazioni supplementari, ma alla classe base queste non servono: la sicurezza di eseguire la conversione risiede nel fatto che la classe derivata gode degli stessi membri di quella base e quindi non si corre il rischio che ci sia riferimento a un membro inesistente. Questo invece si verifica nel caso opposto: se una variabile di tipo Student assumesse il valore di un oggetto Person, School e Year sarebbero privi di valore e ciò generebbe un errore. Per eseguire questo tipo di passaggi è necessario l'operatore DirectCast.
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.



