Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Librerie di classi VB e namespace Global con Visual Studio 2012

Come utilizzare Global in Visual Basic e come estendere i namespace standard con Visual Basic.
Come utilizzare Global in Visual Basic e come estendere i namespace standard con Visual Basic.
Link copiato negli appunti

Dalle raccolte di routine degli anni '80 ad oggi, la storia delle librerie evolve con l'identico scopo di creare codice riutilizzabile.

Con .NET le raccolte sono diventate librerie di classi, cioè assembly o, più colloquialmente, DLL. Queste librerie raggruppano sotto una unica denominazione molte classi, di solito omogenee tra loro, che con i loro metodi permettono di definire del codice riutilizzabile. I vantaggi sono notevoli:

  • per chi crea le librerie, basti pensare alle librerie di componenti di terze parti create per la vendita senza dover distribuire il codice sorgente;
  • per chi le utilizza, perché semplificano il lavoro, riducono i tempi di sviluppo e di test e aumentano la produttività: il codice delle librerie è già testato e non occorre "reinventare l'acqua calda".
  • Creare libreria di classi con Visual Studio

    Naturalmente anche in Visual Studio 2012 possiamo creare delle librerie di classi..

    Visual Studio 2012 fornisce tutti gli strumenti necessari per creare, distribuire e utilizzare librerie di classi. Tra questi strumenti, ora, è compresa anche una nuova funzionalità che è una novità assoluta per Visual Studio: il namespace Global.

    In questo articolo parleremo proprio di questo importantissimo namespace, ma prima facciamo un piccolo ripasso sui namespace standard e su quelli personalizzati (definiti dagli sviluppatori). Vedremo poi cosa cambia con l'introduzione del nuovo namespace Global e perché è così importante.

    I namespace

    I namespace sono concettualmente simili a contenitori e servono a organizzare il codice in unità ben definite. Sono organizzati in modo gerarchico: ciascun namespace può contenere una o più classi, ma anche altri namespace. Ogni namespace di secondo livello può contenere altri namespace e classi e così via, in una struttura gerarchica anche molto complessa e con molti livelli.

    È molto importante organizzare bene la gerarchia di namespace e di classi, perché una gerarchia disordinata provoca confusione nello sviluppatore che utilizza la libreria, causando la perdita di tutti i vantaggi che si volevano ottenere.

    Anche il .NET Framework è organizzato in questo modo, con moltissimi namespace nidificati a più livelli, come le scatole cinesi. Ogni namespace raggruppa tutte le classi che fanno parte di una specifica categoria: per esempio il namespace System.IO contiene tutte le classi che servono per lavorare con i file, le cartelle e i dischi, con i flussi di input e output e così via.

    Qui troviamo anche altri namespace specializzati: Compression, IsolatedStorage, Packaging, Pipes e Ports. A loro volta, questi namespace contengono altre classi associate alla specifica categoria. Per esempio il namespace Compression contiene le classi necessarie a gestire i file compressi.

    Il Default namespace

    Anche se non ce ne rendiamo conto, nei nostri progetti utilizziamo sempre almeno un namespace: il Root namespace (spazio dei nomi "radice"). Nella documentazione disponibile in rete, in alcuni casi potreste trovare questo elemento denominato anche come Default namespace (Spazio dei nomi predefinito), ma lo potete considerare come sinonimo.

    Per verificare che quello che abbiamo affermato è vero, potete aprire qualsiasi soluzione di Visual Studio ed esaminare le proprietà del progetto. Nella scheda Applicazione, infatti, troviamo una proprietà di nome Spazio dei nomi radice:

    Figura 1. La proprietà "Spazio dei nomi radice" di un progetto

    La proprietà "Spazio dei nomi radice" di un progetto

    Come potete vedere in figura, il nome del namespace del progetto è uguale al nome del progetto e dell'assembly, ma potete modificarlo secondo le vostre preferenze (il nome non può iniziare comunque con un carattere numerico).

    Definire i namespace nel codice

    Chiunque può definire nuovi namespace nei progetti Visual Studio, anche nidificandoli in namespace a più livelli. Per esempio possiamo definire un codice come quello seguente:

    Namespace MyNamespace
    
    	Public Class Classe1
    	' qui scriviamo il codice della classe Classe1
    	End Class
    	Namespace MyNamespace2
    
    		Public Class Classe2
    		' qui scriviamo il codice della classe Classe2
    		End Class
    	End Namespace
    
    	Namespace MyNamespace3
    		Public Class Classe3
    		' qui scriviamo il codice della classe Classe3
    		End Class
    
    		Namespace MyNamespace4
    			Public Class Classe4
    			' qui scriviamo il codice della classe Classe4
    			End Class
    
    		End Namespace
    	End Namespace
    End Namespace

    Per accedere alle varie classi dobbiamo utilizzare una sintassi che tenga conto di tutto il "percorso" che è necessario seguire per raggiungere la classe, in modo non molto diverso da quello che si fa normalmente nel file system per indicare un file contenuto in una delle sottocartelle. Per esempio, se vogliamo accedere alla Classe1

    MyNamespace.Classe1

    Per accedere alla Classe4

    MyNamespace.MyNamespace3.MyNamespace4.Classe4

    Poiché i namespace hanno sempre un livello di accesso di tipo Public

    Proviamo ora a vedere un esempio con una applicazione di prova.

    "Metodo tradizionale" in un'applicazione Visual Basic

    Consideriamo come "metodo tradizionale" quello che si è sempre fatto prima dell'introduzione del nuovo namespace Global. In seguito vedremo cosa cambia utilizzando Global.

    Dopo aver creato un'applicazione di tipo WPF (che noi abbiamo chiamato MyLibrary_VS2012_01_VB), inseriamo tre caselle di testo (Valore1, Valore2 e Risultato), tre controlli di tipo Label per le descrizioni e due pulsanti (Somma1 e Somma2), disposti come in figura:

    Figura 2. La prima applicazione di prova
    La prima applicazione di prova

    Il codice XAML è il seguente:

    <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    		xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    		xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    		mc:Ignorable="d"
    		x:Class="MainWindow" Title="MainWindow" Height="330" Width="394">
    
    	<Grid Margin="10,0,2,3">
    		<TextBox x:Name="Valore1" HorizontalAlignment="Left" VerticalAlignment="Top"
    		                          Height="37" TextWrapping="Wrap" Text="0"
    								  Width="180" Margin="140,28,0,0" FontSize="20"/>
    
    		<TextBox x:Name="Valore2" HorizontalAlignment="Left" VerticalAlignment="Top"
                                      Height="37" TextWrapping="Wrap" Text="0" Width="180"
    								  Margin="140,70,0,0" FontSize="20"/>
    		<Button x:Name="Somma1" Content="Somma senza Global" HorizontalAlignment="Left"
    		                                                     VerticalAlignment="Top"
    															 Width="275" Margin="45,124,0,0"
    															 FontSize="20"/>
    
    		<Button x:Name="Somma2" Content="Somma con Global" HorizontalAlignment="Left"
    		                                                   VerticalAlignment="Top"
    														   Width="275" Margin="45,160,0,0"
    														   FontSize="20"/>
            <Label Content="Valore 1" HorizontalAlignment="Left" VerticalAlignment="Top"
    		                          Margin="45,24,0,0" FontSize="20"/>
    
    		<Label Content="Valore 2" HorizontalAlignment="Left" VerticalAlignment="Top"
    		                          Margin="45,66,0,0" FontSize="20"/>
    		<Label Content="Risultato" HorizontalAlignment="Left" VerticalAlignment="Top"
    		                           Margin="45,221,0,0" FontSize="20"/>
    
    		<TextBox x:Name="Risultato" HorizontalAlignment="Left" VerticalAlignment="Top"
    		                            Height="37" TextWrapping="Wrap" Text="0"  Width="180"
    									Margin="140,225,0,0" FontSize="20"/>
    	</Grid>
    </Window>

    L'obiettivo che ci poniamo è quello di sommare i due valori inseriti nelle caselle di testo e inserire il risultato nella terza casella di testo. Ovviamente è un obiettivo semplice da raggiungere e anche poco interessante: proprio per la semplicità del compito possiamo concentrarci meglio sul vero obiettivo del progetto, cioè una migliore definizione di una libreria di classi.

    Aggiungete una classe di nome Class1.vb nel progetto Visual Basic, aprite il file con un doppio clic e inserite il seguente codice:

    ' Esempio: MyLibrary_VS2012_01_VB
    Public Class Class1 
    
    	Public Function Somma(ByVal valore1 As Long, ByVal valore2 As Long) As Long
    		Return valore1 + valore2
    	End Function
    End Class

    Questa classe espone una funzione (Somma Long

    Dopo aver aggiunto una nuova classe di nome Class2.vb, inserite il seguente codice:

    ' Esempio: MyLibrary_VS2012_01_VB
    Namespace Utilities
    
    	Public Class Class2
    		Public Function Somma( ByVal valore1 As Long, ByVal valore2 As Long) As Long
    			Return valore1 + valore2
    		End Function
    	End Class
    End Namespace

    Come potete vedere, Class2 Class1 Utilities

    A prima vista, il namespace Utilities dovrebbe essere un namespace di primo livello, ma come vedremo le cose non stanno esattamente in questi termini.

    Ora dobbiamo attivare i due pulsanti, in modo che premendoli succeda qualcosa di utile.

    Torniamo al file MainWindow.xaml ed eseguiamo un doppio-clic su entrambi i pulsanti, in modo da creare lo schema vuoto dei loro gestori degli eventi Click. Inseriamo quindi il seguente codice:

    ' Esempio: MyLibrary_VS2012_01_VB
    Class MainWindow
    	Private Sub Somma1_Click(sender As Object, e As RoutedEventArgs) _
    				Handles Somma1.Click 
    
    		Risultato.Text = "0"
    		If IsNumeric(Valore1.Text) And IsNumeric(Valore2.Text) Then
    		Dim Utilities As New MyLibrary_VS2012_01_VB.Class1
    			Risultato.Text = Utilities.Somma(Long.Parse(Valore1.Text), Long.Parse(Valore2.Text)).ToString
    
    		Else
    			MessageBox.Show("Uno dei due valori non è numerico")
    		End If
    	End Sub
    
    	Private Sub Somma2_Click(sender As Object, e As RoutedEventArgs) _
    	            Handles Somma2.Click
    		Risultato.Text = "0"
    		If IsNumeric(Valore1.Text) And IsNumeric(Valore2.Text) Then
    			Dim Utilities As New MyLibrary_VS2012_01_VB.Utilities.Class2
    			Risultato.Text = Utilities.Somma(Long.Parse(Valore1.Text), Long.Parse(Valore2.Text)).ToString
    		Else
    			MessageBox.Show("Uno dei due valori non è numerico")
    		End If
    	End Sub
    End Class

    Abbiamo verificato che entrambe le caselle di testo abbiano un valore numerico valido: se entrambi i valori sono validi viene eseguita la somma, se anche un solo valore non è numerico (testo o vuoto) viene visualizzato un messaggio di errore.

    I due metodi sono pressoché uguali, tranne per la terza istruzione che rispettivamente si presenta così:

    Dim Utilities As New MyLibrary_VS2012_01_VB.Class1

    e così:

    Dim Utilities As New MyLibrary_VS2012_01_VB.Utilities.Class2

    Vedremo in seguito il motive di questa differenza. per ora limitiamoci ad avviare l'applicazione. Dopo aver inserito due valori numerici nelle prime due caselle di testo provate a premere il primo pulsante. Se il codice è scritto correttamente, dovremmo vedere nella terza casella di testo il valore risultante dalla somma dei due valori inseriti.

    Lo stesso avverrà premendo il secondo pulsante, a dimostrazione che i due metodi sono assolutamente equivalenti.

    Notiamo, però, che nel codice abbiamo dovuto inserire un riferimento esplicito allo Spazio dei nomi radice (MyLibrary_VS2012_01_VB). Nel secondo caso, poi, vediamo che il namespace Utilities dipende dallo spazio dei nomi radice, cioè è un namespace di secondo livello, mentre noi volevamo che questo diventasse un namespace principale, cioè un namespace di primo livello.

    Nella figura seguente osserviamo la posizione delle classi Class1 e Class2 nella gerarchia di classi dell'applicazione:

    Figura 3. Gerarchia delle classi nella finestra Visualizzazione classi
    Gerarchia delle classi nella finestra Visualizzazione classi

    Continuiamo ora con il namespace Global.

    Il namespace Global

    Cerchiamo ora di migliorare la struttura dell'applicazione utilizzando la parola riservata Global.

    Per prima cosa modifichiamo la dichiarazione delle classi Class1 e Class2 nel seguente modo (il codice all'interno delle due classi rimane invariato):

    ' Esempio: MyLibrary_VS2012_02_VB
    Namespace Global
    	Public Class Class1
    		Public Function Somma(ByVal valore1 As Long, ByVal valore2 As Long) As Long
    			Return valore1 + valore2
    		End Function
    	End Class
    End Namespace
    ' Esempio: MyLibrary_VS2012_02_VB
    Namespace Global.Utilities
    	Public Class Class2
    		Public Function Somma(ByVal valore1 As Long, ByVal valore2 As Long) As Long
    			Return valore1 + valore2
    		End Function
    	End Class
    End Namespace

    Modifichiamo poi le chiamate presenti nel codice dei gestori degli eventi Click dei due pulsanti:

    ' Esempio: MyLibrary_VS2012_02_VB
    Class MainWindow
    	Private Sub Somma1_Click(sender As Object, e As RoutedEventArgs) _
    			    Handles Somma1.Click
    
    		Risultato.Text = "0"
    		If IsNumeric(Valore1.Text) And IsNumeric(Valore2.Text) Then
    			Dim Utilities As New Class1
    			Risultato.Text = Utilities.Somma(Long.Parse(Valore1.Text), Long.Parse(Valore2.Text)).ToString
    		Else
    			MessageBox.Show("Uno dei due valori non è numerico")
    		End If
    	End Sub
    	Private Sub Somma2_Click(sender As Object, e As RoutedEventArgs) _
    	            Handles Somma2.Click
    
    		Risultato.Text = "0"
    		If IsNumeric(Valore1.Text) And IsNumeric(Valore2.Text) Then
    			Dim Utilities As New Utilities.Class2
    			Risultato.Text = Utilities.Somma(Long.Parse(Valore1.Text), Long.Parse(Valore2.Text)).ToString
    		Else
    			MessageBox.Show("Uno dei due valori non è numerico")
    		End If
    	End Sub
    End Class

    La modifica che abbiamo apportato al codice è veramente minima, ma gli effetti sono molto interessanti. Diamo un'occhiata al Visualizzatore classi

    Figura 4. La struttura del progetto Visual Basic modificato
    La struttura del progetto Visual Basic modificato

    la struttura è sostanzialmente identica.

    Cos'è Global?

    Global è una nuova parola riservata che definisce un namespace di primo livello. Potendo essere solo un namespace di primo livello, non può essere utilizzata per definire un namespace nidificato, come nell'esempio seguente:

    ' ### definizione non corretta! ###
    Namespace MiaLibreria
    
    	Namespace Global.NuovaLibreria
    		' ... codice
    
    	End Namespace
    End Namespace

    Evitare le collisioni

    Poiché Global definisce un namespace di primo livello, è possibile definire al suo interno dei namespace di livello inferiore anche con lo stesso nome di altri namespace già esistenti nel .NET Framework, senza che questo sia la causa di "collisioni".

    Supponiamo, per esempio, di scrivere un blocco di codice come quello che segue:

    ' ### definizione non corretta! ###
    Namespace System.Text
    	Class CodificaTesto
    		' ... codice
    	End Class
    End Namespace
    Module Module1
    	Sub Main()
    		Dim codifica As New System.Text.CodificaTesto
    
    		' la seguente istruzione provocherà un'errore
    		' in fase di compilazione, perché il namespace
    		' standard ha perso l'ambito di visibilità:
    		Dim sb As New System.Text.StringBuilder
    
    	End Sub
    End Module

    Mentre IntelliSense mostra di riconoscere la classe System.Text.CodificaTesto System.Text.StringBuilder

    Questo comportamento può apparire strano, ma in realtà è normale: il namespace System.Text che abbiamo creato, infatti, blocca la visibilità del namespace presente nel .NET Framework ed espone solamente il namespace creato localmente.

    Estendere un namespace del .NET Framework con Global

    La soluzione, in questo caso, ci viene dalla parola Global: utilizzandola, infatti, possiamo estendere i namespace del .NET Framework con la massima facilità.

    Proviamo a vedere lo stesso caso con le opportune modifiche.

    ' questa definizione funziona!
    Namespace Global.Math
    	Public Class Operazioni
    		Public Function SommaIntera(ByVal valore1 As Long, ByVal valore2 As Long) As Long
    			Return valore1 + valore2
    		End Function
    	End Class
    End Namespace
    Module Module1
    
    	Sub Main()
    		Dim somma As New Math.Operazioni()
    		somma.SommaIntera(1, 2)
    	End Sub
    End Module

    Dopo la semplice aggiunta di Global alla definizione del namespace, saranno riconosciute sia le classi standard del .NET Framework (per esempio System.Text.StringBuilder, System.Math), sia le estensioni che abbiamo definito nel nostro codice (in questo caso System.Math.Operazioni).

    Concludiamo allora con l'ultimo esempio che permette alla nostra classe Operazioni di diventare un'estensione del namespace System.Math. Per ottenere questo risultato, aggiungiamo qualche modifica in più.

    Creiamo una nuova soluzione Visual Studio e procediamo pressappoco con le stesse modalità utilizzate negli esempi precedenti.

    In questo caso, però, invece di una classe aggiungeremo un modulo (Module1.vb). In questo modulo inseriremo il seguente codice:

    ' Esempio: MyLibrary_VS2012_03_VB
    Namespace Global.System.Math
       Public Class Operazioni
          Public Function SommaIntera(ByVal valore1 As Long, ByVal valore2 As Long) As Long
             Return valore1 + valore2
          End Function
       End Class
    
    End Namespace

    All'interno del modulo abbiamo definito il namespace Global.System.Math Operazioni SommaIntera Long

    Nel file MainWindow.xaml.vb modifichiamo il codice come segue:

    ' Esempio: MyLibrary_VS2012_03_VB
    Class MainWindow
        Private Sub Somma1_Click(sender As Object, e As RoutedEventArgs) _
                    Handles Somma1.Click
    
            If IsNumeric(Valore1.Text) And IsNumeric(Valore2.Text) Then
    		    Dim Utilities As New System.Math.Operazioni()
    		    Risultato.Text = Utilities.SommaIntera(Long.Parse(Valore1.Text), Long.Parse(Valore2.Text)).ToString()
            Else
                MessageBox.Show("Uno dei due valori non è numerico")
            End If
        End Sub
    End Class

    In questo caso abbiamo creato un'istanza della classe System.Math.Operazioni che è esattamente la classe che abbiamo definito noi. Se non fosse per il nome della classe, espresso in lingua italiana, sembrerebbe essere una classe inclusa nativamente nel namespace System.Math.

    Ti consigliamo anche