Guida al Visual Basic .NET
Capitolo 19° - Le Classi Specificatori di accesso
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:
Un esempio praticoRiprendiamo 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 SubLa 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 ModuleIn 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 ModuleIl codice contenuto in Main può accedere a:
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 StruttureAnche 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 Structureche equivale a: Structure Contact Public Name, Surname, PhoneNumber As String End StructureOvviamente, anche le strutture stesse hanno sempre uno scope, così come qualsiasi altra entità del .NET. Un esempio intelligenteEcco 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:
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|