|
Tutte le categorie di membri che abbiamo analizzato nei precedenti capitoli - campi, metodi, proprietà, costruttori - sono sempre
state viste come appartenenti ad un oggetto, ad un'istanza di classe. Infatti, ci si riferisce ad una proprietà o a un metodo di uno
specifico oggetto, dicendo ad esempio "La proprietà SideLength dell'oggetto A di tipo Cube vale 3, mentre quella dell'oggetto B
anch'esso di tipo Cube vale 4.". La classe, ossia il tipo di una variabile reference, ci diceva solo quali membri un certo oggetto
potesse esporre: ci forniva, quindi, il "progetto di costruzione" di un oggetto nella memoria, in cui si potevano collocare tali campi,
tali metodi, tal'altre proprietà e via dicendo.
Ora, un membro shared, o condiviso, o statico (termine da usarsi più in C# che non in VB.NET), non appartiene più ad un'istanza di classe, ma alla classe stessa. Mi rendo conto
che il concetto possa essere all'inizio difficile da capire e da interiorizzare correttamente. Per questo motivo farò un esempio il
più semplice, ma più significativo possibile. Riprendiamo la classe Cube, modificata come segue:
Module Module1
Class Cube
Private _SideLength As Single
Private _Density As Single
'Questo campo è Shared, condiviso. Come vedete,
'per dichiarare un membro come tale, si pone la
'keyword Shared dopo lo specificatore di accesso. Questa
'variabile conterrà il numero di cubi creati
'dal nostro programma.
'N.B.: I campi Shared sono di default Private...
Private Shared _CubesCount As Int32 = 0
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
'Allo stesso modo, la proprietà che espone il membro
'shared deve essere anch'essa shared
Public Shared ReadOnly Property CubesCount() As Int32
Get
Return _CubesCount
End Get
End Property
'Ogni volta che un nuovo cubo viene creato, _CubesCount
'viene aumentato di uno, per rispecchiare il nuovo numero
'di istanze della classe Cube esistenti in memoria
Sub New()
_CubesCount += 1
End Sub
End Class
Sub Main()
Dim Cube1 As New Cube()
Cube1.SideLength = 1
Cube1.Density = 2700
Dim Cube2 As New Cube()
Cube2.SideLength = 0.9
Cube2.Density = 3500
Console.Write("Cubi creati: ")
'Notate come si accede a un membro condiviso: poiché
'appartiene alla classe e non alla singola istanza, vi si
'accede specificando prima il nome della classe, poi
'il comune operatore punto, e successivamente il nome
'del membro. Tutti i membri shared funzionano in questo
'modo
Console.WriteLine(Cube.CubesCount)
Console.ReadKey()
End Sub
End Module
Facendo correre l'applicazione, si vedrà apparire a schermo il numero 2, poiché abbiamo creato due oggetti di tipo Cube. Come
si vede, il campo CubesCount non riguarda un solo specifico oggetto, ma la totalità di tutti gli oggetti di tipo Cube, poiché
è un dato globale. In generale, esso è di dominio della classe Cube, ossia della rappresentazione più astratta
dell'essenza di ogni oggetto: per sapere quanti cubi sono stati creati, non si può interpellare una singola istanza, perchè
essa non "ha percezione" di tutte le altre istanze esistenti. Per questo motivo CubesCount è un membro condiviso.
Anche in questo caso c'è una ristretta gamma di casi in cui è opportuno scegliere di definire un membro come condiviso:
- Quando contiene informazioni riguardanti la totalità delle istanze di una classe, come in questo caso;
- Quando contiene informazioni accessibili e necessarie a tutte le istanze della classe, come illustrerò fra qualche capitolo;
- Quando si tratta di un metodo "di libreria". I cosiddetti metodi di libreria sono metodi sempre shared che svolgono funzioni generali
e sono utilizzabili da qualsiasi parte del codice. Un esempio potrebbe essere la funzione Math.Abs(x), che restituisce il valore assoluto
di x. Come si vede, è shared poiché vi si accede usando il nome della classe. Inoltre, essa è sempre usabile, poiché
si tratta di una semplice funzione matematica, che, quindi, fornisce servizi di ordine generale;
- Quando si trova in un modulo, come spiegherò nel prossimo paragrafo.
Classi Shared
Come!?!? Esistono classi shared? Ebbene sì. Può sembrare assurdo, ma ci sono, ed è lecito domandarsi quale sia la loro
funzione. Se un membro shared appartiene a una classe... cosa possiamo dire di una classe shared?
A dire il vero, abbiamo sempre usato classi shared senza saperlo: i moduli, infatti, non sono altro che classi condivise (o statiche). Tuttavia,
il significato della parola shared, se applicato alle classi, cambia radicalmente. Un modulo, quindi una classe shared, rende implicitamente
shared tutti i suoi membri. Quindi, tutte le proprietà, i campi e i metodi appartenenti ad un modulo - ivi compresa la Sub Main - sono
membri shared. Che senso ha questo? I moduli sono consuetamente usati, al di fuori delle applicazioni console, per rendere disponibili
a tutto il progetto membri di particolare rilevanza o utilità, ad esempio funzioni per il salvataggio dei dati, informazioni sulle
opzioni salvate, eccetera... Infatti è impossibile definire un membro shared in un modulo, poiché ogni membro del modulo lo
è già di per sé:
Module Module1
Shared Sub Hello()
End Sub
'...
End Sub
Il codice sopra riportato provocherà il seguente errore:
Methods in a Module cannot be declared 'Shared'.
Inoltre, è anche possibile accedere a membri di un modulo senza specificare il nome del modulo, ad esempio:
Module Module2
Sub Hello()
Console.WriteLine("Hello!")
End Sub
End Module
Module Module1
Sub Main()
Hello() ' = Module2.Hello()
Console.ReadKey()
End Sub
End Module
Questo fenomeno è anche noto col nome di Imports statico.
A dir la verità esiste una piccola differenza tra classi statiche e moduli. Una classe può essere statica anche solo se
tutti i suoi membri lo sono, ma non gode dell'Imports Statico. Un modulo, al contrario, oltre ad avere tutti i membri shared, gode sempre
dell'Imports Statico. Per farla breve:
Module Module2
Sub Hello()
Console.WriteLine("Hello Module2!")
End Sub
End Module
Class Class2
Shared Sub Hello()
Console.WriteLine("Hello Class2!")
End Sub
End Class
Module Module1
Sub Main()
'Per richiamare l'Hello di Class2, è sempre
'necessaria questa sintassi:
Class2.Hello()
'Per invocare l'Hello di Module2, invece, basta
'questa, a causa dell'Imports Statico
Hello()
Console.ReadKey()
End Sub
End Module
|
|