Casino online











Mercato forex






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:
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 Value As [Tipo])
        'Istruzioni da eseguire quando si sta per assegnare
        'un valore al campo di cui la proprietà è mediatore
    End Set
End Property 
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 =:
'Si può scrivere
[Proprieta'] = [Valore]
'Ma anche
[Qualcosa] = [Proprieta'] 
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!
Module Esempio
    Class 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, _Maximum As 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 UInt32
            Get
                'Nessuna operazione interessante: restituisce
                'semplicemente il valore del campo privato
                Return _Number
            End Get
            Set(ByVal Value As UInt32)
                'Se il valore è minore di _Maximum, allora nessun
                'problema
                If Value < _Maximum Then
                    _Number = Value
                Else
                    'Altrimenti imposta _Number sullo stesso
                    'valore di _Maximum
                    _Number = _Maximum
                End 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 UInt32
            Get
                Return _Maximum
            End Get
            Set(ByVal Value As UInt32)
                If Value <> 0 Then
                    _Maximum = Value
                Else
                    _Maximum = 1
                End 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 / _Maximum
        End Function

        'Il costruttore accetta due parametri, di cui controlla
        'l'assegnamento semplicemente impostando le proprietà
        Public Sub New(ByVal Number As UInt32, ByVal Maximum As 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 = Maximum
            Me.Number = Number
        End Sub
    End Class

    Sub Main()
        Dim P As 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 
Facciamo correre l'applicazione e sullo schermo apparirà:
100
100
25 
Potrebbe 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:
ReadOnly Property [Nome della proprieta']([Eventuali parametri]) As [Tipo]
    Get
        '...
        Return [Valore]
    End Get
End Property 
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:
Module Esempio2
    'Questa classe gestisce ipoteticamente il login di un utente
    Class UserSession
        Private _LastAccess, CreationTime As Date
        Private _LastIP As String
        Private _Username, _Password As String

        'Il nickname dell'utente
        Public Property Username() As String
            Get
                Return _Username
            End Get
            Set(ByVal Value As String)
                _Username = Value
            End 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 _LastAccess
            End Get
        End Property

        'L'ultimo IP: stesso discorso di sopra
        Public ReadOnly Property LastIP() As String
            Get
                Return _LastIP
            End Get
        End Property

        'Inizializza una nuova sessione, scollegata dal server
        Sub New(ByVal Username As String, ByVal Password As String)
            _Username = Username
            _Password = Password
            'Data di questo accesso
            CreationTime = Date.Now
        End 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 DBLastAccess As Date
            Dim DBLastIP As String

            _LastAccess = DBLastAccess
            _LastIP = DBLastIP
        End 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 DB As Object

            DB.User(_Username).LastAccess = CreationTime
            DB.User(_Username).LastIP = GetIP()

            Session.Close()
        End Sub
    End Class
End Module 
Module Esempio3
    Class LogFile
        Private _FileName As String
        Private _CreationTime As 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 _FileName
            End 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 _CreationTime
            End Get
        End Property

        Public Sub New(ByVal Path As String)
            _FileName = Path
            _CreationTime = Date.Now
        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:
Class MyNotes
    Private _Notes As New ArrayList
    
    Public ReadOnly Property Notes() As ArrayList
        Get
            Return _Notes
        End Get
    End Property
    '...
End Class
'...
Dim N As New MyNotes
N.Notes.Add("Ore 2.00: andare a prendere il pane.") 
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.






 

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