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 possiamo utilizzare il riferimento:

MyNamespace.Classe1

Per accedere alla Classe4, invece, dobbiamo utilizzare il riferimento:

MyNamespace.MyNamespace3.MyNamespace4.Classe4

Poiché i namespace hanno sempre un livello di accesso di tipo Public, possiamo accedere liberamente ai namespace da qualsiasi punto del codice.

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) che prende due interi di tipo Long come argomenti di ingresso, e ne restituisce la somma. Come abbiamo già specificato, la semplicità della funzione è voluta per non aggiungere un ulteriore livello di complessità. Naturalmente voi potete definire qualsiasi funzione, anche qualcosa di molto complesso.

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 contiene esattamente le stesse istruzioni di Class1, ma in questo caso è inserita nella dichiarazione del namespace Utilities. Questo nome è inventato, per seguire l'esempio potete scegliere lo stesso nome o quello che preferite.

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, confrontando il precedente progetto con quello che abbiamo appena modificato:

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, possiamo notare che invece non riconosce più la classe System.Text.StringBuilder, una classe del .NET Framework.

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 e, nidificato a questo, la classe Operazioni. Quest'ultima contiene un unico metodo di nome SommaIntera che richiede due argomenti di tipo Long e, a sua volta, restituisce un valore di tipo Long che rappresenta la somma dei due valori di ingresso.

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