Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Membri Shared

Guida al Visual Basic .NET

Capitolo 23° - Membri Shared

<< Precedente Prossimo >>

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 


<< Precedente Prossimo >>
A proposito dell'autore

Programmatore e analista .NET 2005/2008/2010 (in particolare C# e VB.NET), anche nell'implementazione Mono per Linux. Conoscenze approfondite di Pascal, PHP, XML, HTML 4.01/5, CSS 2.1/3, Javascript (e jQuery). Conoscenze buone di C, LUA, GML, Ruby, XNA, AJAX e Assembly 68000. Competenze basilari di C++, SQL, Hlsl, Java.