Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Le Classi Specificatori di accesso

Guida al Visual Basic .NET

Capitolo 19° - Le Classi Specificatori di accesso

<< Precedente Prossimo >>

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.

MemberAccess.jpg

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à


<< Precedente Prossimo >>
A proposito dell'autore

C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...