A23. Le Proprietà - Parte I
Le proprietà sono molto usate nella stesura di codice avanzato, poichè a prima vista potrebbero apparire poco utili. Una proprietà
ha il compito di rendere pubblici campi privati che sono nascosti al client per motivi di sicurezza (con client ci si riferisce a qualsiasi
applicazione, progetto, classe, o in generale assemebly che utilizza una data classe). Per questo motivo esse sono anche definite wrapper
di campi privati, ossia "contenitori" (dall'inglese wrap = impacchettare): si "avvolgono" attorno a un campo e fanno da mediatori
nell'assegnazione e nella lettura dei valori. La sintassi generale con cui si dichiara una proprietà è questa:
Essendo in sola lettura, le proprietà ReadOnly presentano solo il blocco Get:
Come si vede l'intestazione di una proprietà è simile a quella di una funzione, così come il corpo del blocco Get, mentre quello del blocco Set è più simile a una procedura. Una proprietà si può quindi vedere come un "campo intelligente", poichè può essere assegnata o letta, ma quando ciò accade viene eseguito un codice che permette di processarne il valore, offrendo così più gestibilità all'autore della classe. L'uso di una proprietà è esattamente identico a quello di una comunissima variabile: si ottiene il valore usando il suo nome e lo si imposta mediante l'operatore =:Property [Nome della proprieta']([Eventuali parametri])As [Tipo]Get 'Istruzioni da eseguire quando viene richiesto il 'valore del campo di cui la proprietà è mediatre Return [Valore]End Get Set (ByVal ValueAs [Tipo]) 'Istruzioni da eseguire quando si sta per assegnare 'un valore al campo di cui la proprietà è mediatore End Set End Property
'Il codice seguente implementa una classe molto semplice, Percent, che, dato un numero qualsiasi e un numero massimo, calcola il valore percentuale del primo rispetto al secondo, usando la classica proporzione numero : massimo = x : 100. Bisogna fare attenzione che il numero non superi il valore massimo!Si può scrivere [Proprieta'] = [Valore] 'Ma anche [Qualcosa] = [Proprieta']
Facciamo correre l'applicazione e sullo schermo apparirà:Module EsempioClass Percent 'I valori privati avvolti da una proprietà iniziano 'di solito con un carattere underscore "_", oppure con 'una lettera minuscola seguita da un underscore Private _Number, _MaximumAs UInt32 'Ecco la prima proprietà in azione: prima 'dell'assegnamento controlla che Value sia 'minore di Maximum 'Da notare che di solito, il nome della proprietà 'corrisponde a quello del campo che avvolge, ma senza 'underscore Public Property Number()As UInt32Get 'Nessuna operazione interessante: restituisce 'semplicemente il valore del campo privato Return _NumberEnd Get Set (ByVal ValueAs UInt32) 'Se il valore è minore di _Maximum, allora nessun 'problema If Value < _MaximumThen _Number = ValueElse 'Altrimenti imposta _Number sullo stesso 'valore di _Maximum _Number = _MaximumEnd If End Set End Property 'La seconda proprietà deve controllare che _Maximum sia 'diverso da 0, poichè il metodo Calculate dovrà 'dividere per questo valore e la divisione per 0 genera 'un errore Public Property Maximum()As UInt32Get Return _MaximumEnd Get Set (ByVal ValueAs UInt32)If Value <> 0Then _Maximum = ValueElse _Maximum = 1End If End Set End Property 'Calcola la percentuale: è indifferente scegliere se usare 'i campi privati o le proprietà all'interno della stessa 'classe, poichè si è comunque sicuri che l'assegnamento è 'avvenuto in modo corretto Public Function Calculate()As Single Return _Number * 100 / _MaximumEnd Function 'Il costruttore accetta due parametri, di cui controlla 'l'assegnamento semplicemente impostando le proprietà Public Sub New (ByVal NumberAs UInt32,ByVal MaximumAs UInt32) 'Da notare che: '1- Me.Number si riferisce alla proprietà Number, 'mentre il solo nome Number indica il parametro Number '2- Il costruttore inizializza prima Maximum, dato che 'di default è 0, per evitare che venga impostato a 0 'anche Number Me .Maximum = MaximumMe .Number = NumberEnd Sub End Class Sub Main()Dim PAs New Percent(200, 100) Console.WriteLine(P.Calculate) P.Maximum = 0 P.Number = 100 Console.WriteLine(P.Calculate) P.Maximum = 200 P.Number = 50 Console.WriteLine(P.Calculate) Console.ReadKey()End Sub End Module
100 100 25Potrebbe sembrare strano, ma il codice di Main genera volutamente le due eccezioni che le proprietà sono state incaricate di risolvere. Analizziamo il codice:
- New Percent(200, 100) crea un nuovo oggetto Percent, assegnando 100 a Maximum e 200 a Number. La prima assegnazione avviene senza errori: 100 è diverso da 0 e viene depositato nel campo privato _Maximum. La seconda invece prende una strada diversa: 200 è maggiore di 100, perciò la proprietà si cura di impostare _Number allo stesso valore di _Maximum, come è scritto nel sorgente. Il risultato è che sia _Number che _Maximum hanno lo stesso valore: Number è quindi il 100% di Maximum.
- P.Maximum = 0, P.Number = 100: _Maximum non può essere 0, quindi la proprietà lo imposta a 1; 100 è maggiore di 1, quindi anche _Number viene impostato a 1. Come nel caso precedente Number è il 100% di Maximum
- P.Maximum = 200, P.Number = 50: 200 è diverso da 0, quindi _Maximum viene impostato su 200; 50 è minore di 200, e _Number diventa 50. Number è il 25% di Maximum (50 è il 25% di 200).
Proprietà ReadOnly e WriteOnly
Le proprietà ReadOnly sono in sola lettura, perciò non è possibile modificarne il valore tranne che usando i costruttori statici, che vedremo
più avanti. Allo stesso modo quelle WriteOnly sono in sola scrittura e non è possibile ottenerne il valore: io stesso mi sono chiesto più
volte a cosa possano servire, ma la realtà è che sono innaturali per la stragrande maggiorandza dei programmatori; piuttosto di usare
proprietà WriteOnly è meglio definire procedure. Mi occuperò quindi di trattare solo la keyword ReadOnly.Essendo in sola lettura, le proprietà ReadOnly presentano solo il blocco Get:
Esse sono spesso usate per fornire un accesso limitato a campi privati per un paio di motivi: i campi non devono essere modificati dal client, poichè il loro valore è di vitale importanza per la corretta esecuzione dei metodi all'interno della classe, tuttavia si dà la possibilità di ottenerne il valore; i campi non devono in alcun modo essere modificati, nè dal client nè dalla classe stessa, poichè contengono dati inizializzati durante l'esecuzione del costruttore. Ecco un esempio di ognuno dei due casi:ReadOnly Property [Nome della proprieta']([Eventuali parametri])As [Tipo]Get '... Return [Valore]End Get End Property
Module Esempio2 'Questa classe gestisce ipoteticamente il login di un utente Class UserSessionPrivate _LastAccess, CreationTimeAs Date Private _LastIPAs String Private _Username, _PasswordAs String 'Il nickname dell'utente Public Property Username()As String Get Return _UsernameEnd Get Set (ByVal ValueAs String ) _Username = ValueEnd Set End Property 'La data del suo ultimo accesso: è naturale che essa 'non possa essere modificata dall'utente, tuttavia l'applicazione 'lo fa ogni volta che l'utente di disconnette Public ReadOnly Property LastAccess()As Date Get Return _LastAccessEnd Get End Property 'L'ultimo IP: stesso discorso di sopra Public ReadOnly Property LastIP()As String Get Return _LastIPEnd Get End Property 'Inizializza una nuova sessione, scollegata dal server Sub New (ByVal UsernameAs String ,ByVal PasswordAs String ) _Username = Username _Password = Password 'Data di questo accesso CreationTime =Date .NowEnd Sub 'Tenta di connettersi e controlla la validità del 'nome utente e della password Public Sub Login() 'Istruzioni di controllo '... 'Ammettiamo che queste variabili siano prese da 'un ipotetico database: Dim DBLastAccessAs Date Dim DBLastIPAs String _LastAccess = DBLastAccess _LastIP = DBLastIPEnd Sub 'Si disconnette, e invia al database la data di questa 'connessione e l'IP attuale 'GetIP e Session.Close() sono procedure immaginarie Public Sub Disconnect() 'Ammettiamo che questo sia il database Dim DBAs Object DB.User(_Username).LastAccess = CreationTime DB.User(_Username).LastIP = GetIP() Session.Close()End Sub End Class End Module
C'è ancora un'ultima, ma importante, clausola da far notare per le proprietà ReadOnly. Si è già vista la differenza tra i tipi value e i tipi reference: i primi contengono un valore, mentre i secondi un puntatore all'area di memoria in cui risiede l'oggetto voluto. A causa di questa particolare struttura, leggere il valore di un tipo reference da una proprietà ReadOnly significa saperne l'indirizzo, il che equivale ad ottenere il valore dell'oggetto puntato. Non è quindi assolutamente sbagliato scrivere:Module Esempio3Class LogFilePrivate _FileNameAs String Private _CreationTimeAs Date 'Niente deve modificare il nome del file, altrimenti 'potrebbero verificarsi errori nella lettura o scrittura 'dello stesso, oppure si potrebbe chiudere un file 'che non esiste ancora Public ReadOnly Property FileName()As String Get Return _FileNameEnd Get End Property 'Allo stesso modo non si può modificare la data di creazione 'di un file: una volta creato, viene prelevata l'ora e il 'giorno e impostata la variabile. Se potesse essere modificata 'non avrebbe più alcun significato Public ReadOnly Property CreationTime()As Date Get Return _CreationTimeEnd Get End Property Public Sub New (ByVal PathAs String ) _FileName = Path _CreationTime =Date .NowEnd Sub End Class End Module
Ma anzi costituisce una vera raffinatezza, in quanto si proibisce l'assegnamento a Notes di un arraylist, determinando possibili modifiche indesiderate dovute alle peculiarità dei tipi reference, e allo stesso tempo si forniscono i mezzi per modificarlo indirettamente.Class My NotesPrivate _NotesAs New ArrayListPublic ReadOnly Property Notes()As ArrayListGet Return _NotesEnd Get End Property '... End Class '... Dim NAs New My Notes N.Notes.Add("Ore 2.00: andare a prendere il pane.")
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.



