Oppure

Loading

Proprietà ReadOnly e WriteOnly

Fin'ora abbiamo visto che le proprietà sono in grado di mediare l'interazione tra codice esterno alla classe e suoi campi, e tale mediazione comprendeva la possibilità di rifiutare certi valori e consentirne altri. Ma non è finita qui: usando delle apposite keywords è possibile rendere una proprietà a sola lettura (ossia è possibile leggerne il valore ma non modificarlo) o a sola scrittura (ossia è possibile modificarne il valore ma non ottenerlo). Per quanto riguarda la prima, viene abbastanza naturale pensare che ci possano essere valori solo esposti verso cui è proibita la manipolazione diretta, magari perché particolarmente importanti o, più spesso, perchè logicamente immutabili (vedi oltre per un esempio); spostando l'attenzione per un attimo sulla seconda, però, sarà parimenti del tutto lecito domandarsi quale sia la loro utilità. Le variabili, i campi, e quindi, per estensione, anche le proprietà, sono per loro natura atti a contenere dati, che verranno poi utilizzati in altre parti del programma: tali dati vengono continuamente letti e/o modificati e, per quanto sia possibile credere che ve ne siano di immodificabili, come costanti e valori a sola lettura, appare invece assurda l'esistenza di campi solo modificabili. Per modificare qualcosa, infatti, se ne deve conoscere almeno qualche informazione. La realtà è che le proprietà WriteOnly sono innaturali per la stragrande maggiorandza dei programmatori; piuttosto di usarle è meglio definire procedure. Mi occuperò quindi di trattare solo la keyword ReadOnly.
In breve, la sintassi di una proprietà a sola lettura è questa:
ReadOnly Property [Nome]() As [Tipo]
    Get
        '...
        Return [Valore]
    End Get
End Property 
Notate che il blocco Set è assente: ovviamente, si tratta di codice inutile dato che la proprietà non può essere modificata. Per continuare il discorso iniziato prima, ci sono principalmente tre motivi per dichiarare un'entità del genere:
  • I dati a cui essa fornisce accesso sono importanti per la vita della classe, ed è quindi necessario lasciare che la modifica avvenga tramite altri metodi della classe stessa. Tuttavia, non c'è motivo di nasconderne il valore al codice esterno, cosa che può anche rivelarsi molto utile, sia come dato da elaborare, sia come informazione di dettaglio;
  • La proprietà esprime un valore che non si può modificare perchè per propria natura immutabile. Un classico esempio può essere la data di nascita di una persona: tipicamente la si inserisce come parametro del costruttore, o la si preleva da un database, e viene memorizzata in un campo esposto tramite proprietà ReadOnly. Questo è logico, poiché non si può cambiare la data di nascita; è quella e basta. Un caso particolare sarebbe quello di un errore commesso durante l'inserimento della data, che costringerebbe a cambiarla. In questi casi, la modifica avviene per altre vie (metodi con autenticazione o modifica del database);
  • La proprietà esprime un valore che viene calcolato al momento. Questo caso è molto speciale, poiché va al di là della normale funzione di wrapper che le proprietà svolgono normalmente. Infatti, si può anche scrivere una proprietà che non sovrintende ad alcun campo, ma che, anzi, crea un campo fittizio: ossia, da fuori sembra che ci sia un'informazione in più nella classe, ma questa viene solo desunta o interpolata da altri dati noti. Esempio:
    Class Cube
        Private _SideLength As Single
        Private _Density As Single
        
        Public Property SideLength() As Single
            Get
                Return _SideLength
            End Get
            Set(ByVal value As Single)
                If value > 0 Then
                    _SideLength = value
                Else
                    _SideLength = 1
                End If
            End Set
        End Property
        
        Public Property Density() As Single
            Get
                Return _Density
            End Get
            Set(ByVal value As Single)
                If value > 0 Then
                    _Density = value
                Else
                    _Density = 1
                End If
            End Set
        End Property
        
        Public ReadOnly Property SurfaceArea() As Single
            Get
                Return (SideLength ^ 2)
            End Get
        End Property
        
        Public ReadOnly Property Volume() As Single
            Get
                Return (SideLength ^ 3)
            End Get
        End Property
        
        Public ReadOnly Property Mass() As Single
            Get
                Return (Volume * Density)
            End Get
        End Property
    End Class 
    Vedendola dall'esterno, si può pensare che la classe Cube contenga come dati concreti (variabili) SideLength, Density, SurfaceArea, Volume e Mass, e che questi siano esposti tramite una proprietà. In realtà essa ne contiene solo i primi due e in base a questi calcola gli altri.
In questo esempio teorico, le due proprietà esposte sono readonly per il primo e il secondo motivo:
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  


Una nota sui tipi reference

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 ASystem
    Private _Box As Cube
    
    Public ReadOnly Property Box() As Cube
        Get
            Return _Box
        End Get
    End Property
    
    '...
End Class

'...

Dim S As New ASystem()
S.Box.SideLength = 4 
In questo modo, noi staimo effettivamente modificando l'oggetto S.Box, ma indirettamente: non stiamo, invece, cambiando il valore del campo S._Box, che effettivamente è ciò che ci viene impedito di fare. In sostanza, non stiamo assegnando un nuovo oggetto alla variabile S._Box, ma stiamo solo manipolando i dati di un oggetto esistente, e questo è consentito. Anzi, è molto meglio dichiarare proprietà di tipo reference come ReadOnly quando non è necessario assegnare o impostare nuovi oggetti.

A cura di: Il Totem