|
Le classi possono possedere molti membri, di svariate categorie, e ognuno di questi è sempre contraddistinto da un livello di accesso.
Esso specifica "chi" può accedere a quali membri, e da quale parte del codice. Molto spesso, infatti, è necessario precludere
l'accesso a certe parti del codice da parte di fruitori esterni: fate bene attenzione, non sto parlando di protezione del codice, di sicurezza,
intendiamoci bene; mi sto riferendo, invece, a chi userà il nostro codice (fossimo anche noi stessi). I motivi sono disparati, ma molto
spesso si vuole evitare che vengano modificate variabili che servono per calcoli, operazioni su file, risorse, eccetera. Al contrario, è
anche possibile espandere l'accesso ad un membro a chiunque. Con questi due esempi introduttivi, apriamo la strada agli specificatori
di accesso, parole chiave anteposte alla dichiarazione di un membro che ne determinano il livello di accesso.
Ecco una lista degli specificatori di accesso esistenti, di cui prenderò ora in esame solo i primi due:
- Private: un membro privato è accessibile solo all'interno della classe in cui è stato dichiarato;
- Public: un membro pubblico è accessibile da qualsiasi parte del codice (dalla stessa classe, dalle sottoclassi, da classi esterne,
perfino da programmi esterni);
- Friend
- Protected
- Protected Friend (esiste solo in VB.NET)
Un esempio pratico
Riprendiamo il codice della classe Cube riproposto nel capitolo precedente. Proviamo a scrivere nella Sub Main questo codice:
Sub Main()
Dim A As New Cube(2700, 1.5)
A.SideLength = 3
End Sub
La riga "A.SideLength = 3" verrà sottolineata e apparirà il seguente errore nel log degli errori:
ConsoleApplication2.Module1.Cube.SideLength' is not accessible in this
context because it is 'Private'.
Questo è il motivo per cui ho usato una procedura per impostare i valori: l'accesso al membro (in questo caso "campo", in quanto si
tratta di una variabile) SideLength ci è precluso se tentiamo di accedervi da un codice esterno alla classe, poiché, di default,
nelle classi, Dim equivale a Private. Dichiarandolo esplicitamente, il codice di Cube sarebbe stato così:
Module Module1
Class Cube
'Quando gli specificatori di accesso sono anteposti alla
'dichiarazione di una variabile, si toglie il "Dim"
Private SideLength As Single
Private Density As Single
Sub New(ByVal SideLengthValue As Single, ByVal DensityValue As Single)
SideLength = SideLengthValue
Density = DensityValue
End Sub
Function GetSurfaceArea() As Single
Return (SideLength ^ 2)
End Function
Function GetVolume() As Single
Return (SideLength ^ 3)
End Function
Function GetMass() As Single
Return (Density * GetVolume())
End Function
End Class
'...
End Module
In questo specifico caso, sarebbe stato meglio impostare tali variabili come Public, poiché nel loro scope (= livello di accesso)
attuale non servono a molto e, anzi, richiedono molto più codice di gestione. Ma immaginate una classe che compia operazioni crittografiche
sui dati che gli sono passati in input, usando variabili d'istanza per i suoi calcoli: se tali variabili fossero accessibili al di fuori della
classe, lo sviluppatore che non sapesse esattamente cosa farci potrebbe compromettere seriamente il risultato di tali operazioni, e quindi
danneggiare i protocolli di sicurezza usati dall'applicazione. Etichettare un membro come private equivarrebbe scherzosamente a porvi sopra
un grande cartello con scritto "NON TOCCARE".
Ma veniamo invece a Public, uno degli scope più usati nella scrittura di una classe. Di solito, tutti i membri che devono essere
resi disponibili per altre parti del programma o anche per altri programmatori (ad esempio, se si sta scrivendo una libreria che sarà
usata successivamente da altre persone) sono dichiarati come Public, ossia sempre accessibili, senza nessun permesso di sorta.

E che dire, allora, dei membri senza specificatore di accesso? Non esistono, a dirla tutta. Anche quelli che nel codice non vengono esplicitamente
marcati dal programmatore con una delle keyword sopra elencate hanno uno scope predefinito: si tratta di Friend. Esso ha un compito particolare
che potrete capire meglio quando affronteremo la scrittura di una libreria di classi: per ora vi basterà sapere che, all'interno
di uno stesso progetto, equivale a Public.
Un'altra cosa importante: anche le classi (e i moduli) sono contraddistinte da un livello di accesso, che segue esattamente le stesse regole
sopra esposte. Ecco un esempio:
Public Class Classe1
Private Class Classe2
'...
End Class
Class Classe3
'...
End Class
End Class
Class Classe4
Public Class Classe5
Private Class Classe6
'...
End Class
End Class
End Class
Module Module1
Sub Main()
'...
End Sub
End Module
Il codice contenuto in Main può accedere a:
- Classe1, perchè è Public
- Classe3, perchè è Friend, ed è possibile accedere al suo contenitore Classe1
- Classe4, perchè è Friend
- Classe5, perchè è Public, ed è possibile accedere al suo contenitore Classe4
mentre non può accedere a:
- Classe2, perchè è Private
- Classe6, perchè è Private
d'altra parte, il codice di Classe2 può accedere a tutto tranne a Classe6 e viceversa.
N.B.: Una classe può essere dichiarata Private solo quando si trova all'interno di un'altra classe (altrimenti non sarebbe mai accessibile,
e quindi inutile).
Specificatori di accesso nelle Strutture
Anche per i membri di una struttura, così come per quelli di una classe, è possibile specificare tutti gli scope esistenti.
C'è solo una differenza: quando si omette lo scope e si lascia una variabile dichiarata solo con Dim, essa è automaticamente
impostata a Public. Per questo motivo ci era possibile accedere ai campi della struttura Contact, ad esempio:
Structure Contact
Dim Name, Surname, PhoneNumber As String
End Structure
che equivale a:
Structure Contact
Public Name, Surname, PhoneNumber As String
End Structure
Ovviamente, anche le strutture stesse hanno sempre uno scope, così come qualsiasi altra entità del .NET.
Un esempio intelligente
Ecco un esempio di classe scritta utilizzando gli specificatori di accesso per limitare l'accesso ai membri da parte del codice di Main (e
quindi da chi usa la classe, poiché l'utente finale può anche essere un altro programmatore). Oltre a questo troverete anche
un esempio di un diffuso e semplice algoritmo di ordinamento, 2 in 1!
Module Module1
'Dato che usiamo la classe solo in questo programma, possiamo
'evitare di dichiararla Public, cosa che sarebbe ideale in
'una libreria
Class BubbleSorter
'Enumeratore pubblico: sarà accessibile da tutti. In questo
'caso è impossibile dichiararlo come Private, poiché
'uno dei prossimi metodi richiede come parametro una
'variabile di tipo SortOrder e se questo fosse private,
'non si potrebbe usare al di fuori della classe, cosa
'che invece viene richiesta.
Public Enum SortOrder
Ascending 'Crescente
Descending 'Decrescente
None 'Nessun ordinamento
End Enum
'Mantiene in memoria il senso di ordinamento della lista,
'per evitare di riordinarla nel caso fosse richiesto due
'volte lo stesso
Private CurrentOrder As SortOrder = SortOrder.None
'Mantiene in memoria una copia dell'array, che è
'accessibile ai soli membri della classe. In
'questo modo, è possibile eseguire tutte
'le operazioni di ordinamento usando un solo metodo
'per l'inserimento dell'array
Private Buffer() As Double
'Memorizza in Buffer l'array passato come parametro
Public Sub PushArray(ByVal Array() As Double)
'Se Buffer è diverso da Nothing, lo imposta
'esplicitamente a Nothing (equivale a distruggere
'l'oggetto)
If Buffer IsNot Nothing Then
Buffer = Nothing
End If
'Copia l'array: ricordate come si comportano i tipi
'reference e pensate a quali ripercussioni tale
'comportamento potrà avere sul codice
Buffer = Array
'Annulla CurrentOrder
CurrentOrder = SortOrder.None
End Sub
'Procedura che ordina l'array secondo il senso specificato
Public Sub Sort(ByVal Order As SortOrder)
'Se il senso è None, oppure è uguale a quello corrente,
'è inutile proseguire, quindi si ferma ed esce
If (Order = SortOrder.None) Or (Order = CurrentOrder) Then
Exit Sub
End If
'Questa variabile tiene conto di tutti gli scambi
'effettuati
Dim Occurrences As Int32 = 0
'Il ciclo seguente ordina l'array in senso crescente:
'se l'elemento i è maggiore dell'elemento i+1,
'ne inverte il posto, e aumenta il contatore di 1.
'Quando il contatore rimane 0 anche dopo il For,
'significa che non c'è stato nessuno scambio
'e quindi l'array è ordinato.
Do
Occurrences = 0
For I As Int32 = 0 To Buffer.Length - 2
If Buffer(I) > Buffer(I + 1) Then
Dim Temp As Double = Buffer(I)
Buffer(I) = Buffer(I + 1)
Buffer(I + 1) = Temp
Occurrences += 1
End If
Next
Loop Until Occurrences = 0
'Se l'ordine era discendente, inverte l'array
If Order = SortOrder.Descending Then
Array.Reverse(Buffer)
End If
'Memorizza l'ordine
CurrentOrder = Order
End Sub
'Restituisce l'array ordinato
Public Function PopArray() As Double()
Return Buffer
End Function
End Class
Sub Main()
'Crea un array temporaneo
Dim a As Double() = {1, 6, 2, 9, 3, 4, 8}
'Crea un nuovo oggetto BubbleSorter
Dim b As New BubbleSorter()
'Vi inserisce l'array
b.PushArray(a)
'Invoca la procedura di ordinamento
b.Sort(BubbleSorter.SortOrder.Descending)
'E per ogni elemento presente nell'array finale
'(quello restituito dalla funzione PopArray), ne stampa
'il valore a schermo
For Each n As Double In (b.PopArray())
Console.Write(n & " ")
Next
Console.ReadKey()
End Sub
End Module
Ricapitolando...
Ricapitolando, quindi, davanti a ogni membro si può specificare una keyword tra Private, Public e Friend (per quello che abbiamo
visto in questo capitolo), che ne limita l'accesso. Nel caso non si specifichi nulla, lo specificatore predefinito varia a seconda dell'entità
a cui è stato applicato, secondo questa tabella:
- Private per variabili contenute in una classe
- Public per variabili contenute in una struttura
- Friend per tutte le altre entità
|
|