Questo articolo potrebbe contenere URL non più validi perché collegamenti a siti o pagine non più presenti. Per mantenere la fluidità dell'articolo, gli URL sono stati mantenuti disabilitando però i collegamenti corrispondenti.


Alla scoperta dei nuovi avanzamenti del linguaggio Visual Basic 2005

Matthew MacDonald

Gli sviluppatori Microsoft stanno lavorando alacremente per mettere a punto una nuova versione di .NET, ma che cosa ne sarebbe della nuova piattaforma di programmazione senza un'ottimizzazione del linguaggio? In questo articolo Matthew MacDonald descrive le modifiche che verranno introdotte nella nuova versione di Visual Basic, al momento chiamata Visual Basic 2005.

(L'articolo è basato su materiale estratto dal libro di Matthew MacDonald, VB 2005 Developer's Notebook, che uscirà prossimamente, edito da O'Reilly).

Quando .NET 1.0 ha fatto la sua comparsa, ha rivoluzionato il modo di costruire le moderne applicazioni e ha introdotto modifiche significative nel linguaggio Visual Basic per la prima volta in circa un decennio. Ad esempio, attività comuni quali la creazione di oggetti, la dichiarazione di strutture, la visualizzazione di form e l'utilizzo di matrici hanno subito cambiamenti sostanziali. (Per una panoramica delle modifiche introdotte in Visual Basic .NET rispetto a Visual Basic 6.0, e anche qualche parere un po' contrariato, vedere www.mvps.org/vb/index2.html?rants/vfred.htm, in inglese).

Per fortuna per quanto riguarda .NET 2.0 non ci si aspettano le stesse scioccanti novità. Al contrario, il linguaggio verrà perfezionato salvaguardando il codice esistente. Molte delle modifiche consistono in funzioni duplicate da C#, nonostante siano presenti anche alcune funzionalità completamente nuove per entrambi i linguaggi. In questo articolo si parlerà delle modifiche più utili, fra cui:

Una funzionalità di cui non si parlerà in questo articolo è il nuovo oggetto My, una specie di repository centrale di funzionalità utili. L'oggetto My non rappresenta una vera e propria modifica del linguaggio Visual Basic: si tratta più che altro di una serie di collegamenti integrati in VB. In futuro verranno pubblicati articoli che approfondiscono l'argomento.

Overload degli operatori

Gli operatori aritmetici come quelli utilizzati per l'addizione (+), la sottrazione (-), la divisione (/) e la moltiplicazione (*) sono ben noti. In VB.NET 1.0 questi operatori vengono applicati a tipi numerici e in genere non hanno alcun significato se utilizzati con altri oggetti. In C# tuttavia non funziona così, gli sviluppatori possono costruire classi personalizzate che supportino in blocco questi operatori (e alcuni altri) per il proprio utilizzo.

Come ha succintamente dichiarato uno degli sviluppatori che ha lavorato alla compilazione di VB.NET, "L'overload degli operatori non è stato incluso in VB.NET 1.0 perché purtroppo non ne abbiamo avuto il tempo". La lacuna viene colmata in VB 2005.

Per capire come funziona l'overload degli operatori, può essere d'aiuto prendere in esame un semplice esempio. Osservare il codice seguente, che non sarebbe valido in VB.NET 1.0.

Dim ShortSpan, LongSpan, TotalSpan As TimeSpan
ShortSpan = TimeSpan.FromMinutes(100)
LongSpan = TimeSpan.FromDays(14)
TotalSpan = ShortSpan + LongSpan
Console.WriteLine(TotalSpan.TotalMinutes)

La riga importante è la penultima, nella quale viene utilizzato l'operatore + per sommare due oggetti TimeSpan. La motivazione per cui funziona è la seguente: quando il compilatore analizza l'espressione "ShortSpan + LongSpan", individua che ShortSpan ha come tipo TimeSpan e che TimeSpan definisce l'operatore +, che non è altro che una funzione speciale. Grazie a questa conoscenza, il codice, la cui sintassi sarebbe comunque valida in VB.NET 1.0, viene tradotto come riportato di seguito:

TotalSpan = ShortSpan.Add(LongSpan)

Per eseguire l'overload di un operatore in Visual Basic 2005 è necessario creare nella classe o struttura un metodo operatore speciale. Tale metodo deve essere dichiarato con le parole chiave Public Shared Operator, seguite dal simbolo dell'operatore (ad esempio, +). Nell'esempio riportato di seguito un metodo operatore aggiunge il supporto per l'operatore dell'addizione:

Public Shared Operator+(objA As MyClass, 
  objB as MyClass) As MyClass
    ' (Code goes here.)
End Operator

Ogni metodo operatore accetta due parametri che rappresentano i valori ai lati dell'operatore. A seconda della classe e dell'operatore, come nel caso della divisione, può essere importante l'ordine.

L'overload dell'operatore potrebbe non avere senso per molti oggetti business, ma è estremamente pratico per modellare strutture matematiche quali vettori, matrici, numeri complessi o frazioni. Nel Listato 1 viene illustrato come eseguire l'overload di operatori aritmetici in Visual Basic per poter aggiungere e sottrarre un oggetto di base Point, come quello definito nello spazio dei nomi GDI+ di .NET Framework.

Listato 1. Classe Point con overload dell'operatore

Public Class Point

    Public X As Integer
    Public Y As Integer

    Public Sub New(ByVal x As Integer, _
      ByVal y As Integer)
        Me.X = x
        Me.Y = y
    End Sub

    Public Shared Operator +(ByVal x As Point, _
      ByVal y As Point) As Point
        Return New Point(x.X + y.X, x.Y + y.Y)
    End Operator

    Public Shared Operator -(ByVal x As Fraction, _
      ByVal y As Fraction) As Point
        Return New Point(x.X - y.X, x.Y - y.Y)
    End Operator

    ' Get a string representation of the Point.
    Public Overrides Function ToString() As String
        Return "(" & Me.X.ToString & ", " & _
          Me.Y.ToString & ")"
    End Function

End Class

Di solito i parametri e il valore restituito di un metodo operatore utilizzano lo stesso tipo. Non sussiste tuttavia alcuna ragione per cui non debba essere possibile creare più versioni di un metodo operatore (overload) in modo da poter utilizzare l'oggetto in espressioni con tipi diversi.

Classi parziali

Le classi parziali non sono strepitose quanto l'overload degli operatori, ma sono comunque utili, in quanto consentono di suddividere una classe in file distinti. Basta definire la stessa classe in più posizioni e avere cura di includere la parola chiave Partial. Di seguito è riportato un esempio in cui la classe TestClass è definita in due parti:

Partial Public Class TestClass
    Public Sub TestMethodA()
        Console.WriteLine("Method A called.")
    End Sub
End Class

Partial Public Class TestClass
    Public Sub TestMethodB()
        Console.WriteLine("Method B called.")
    End Sub
End Class

Nell'esempio, le due dichiarazioni sono contenute nello stesso file, una dopo l'altra. Non esiste tuttavia alcuna ragione per cui non debba essere possibile inserire le due parti della classe TestClass in file di codice sorgente diversi all'interno dello stesso progetto. Le sole limitazioni sussistenti sono l'impossibilità di definire le due parti in assembly o spazi dei nomi distinti. Non esiste inoltre alcuna ragione per cui non debba essere possibile creare più parti per TestClass: di fatto non vi sono limiti.

Quando si genera l'applicazione, in Visual Studio .NET viene tenuta traccia delle varie parti di TestClass, la quale viene assemblata in una classe completa e compilata con due metodi: TestMethodA() e TestMethodB(). Entrambi i metodi possono essere utilizzati nello stesso modo, come illustrato di seguito:

Dim Obj As New TestClass()
Obj.TestMethodA()
Obj.TestMethodB()

Da un punto di vista generale, le classi parziali hanno due finalità:

Il secondo punto è estremamente importante ed è il motivo per il quale le classi parziali sono state introdotte nei linguaggi di .NET come VB 2005 e C#. Ad esempio, quando si crea un form .NET in Visual Studio 2005, il codice di gestione eventi viene inserito nel file del codice sorgente del form, ma il codice di progettazione che crea e configura i singoli controlli e connette i gestori eventi non è visualizzato. Ciò avviene perché tale codice viene inserito in un file distinto non visibile, in modo che non possa essere inavvertitamente alterato. Ovviamente è possibile visualizzare il codice se proprio si desidera essere rassicurati. Per farlo, scegliere Progetto | Mostra tutti i file dal menu di Visual Studio. Eseguendo questa operazione viene visualizzato un file contenente l'altra metà della classe. Ad esempio, se si crea un nuovo form denominato Form1, verranno creati il file Form1.vb contenente il codice e il file Form1.Designer.vb contenente la parte generata automaticamente. Una nota finale: queste funzionalità non sono ancora implementate in tutte le prime versioni alfa di Visual Studio 2005.

Istruzione Continue

Il linguaggio Visual Basic dispone di alcune istruzioni di controllo del flusso che consentono di dirigere l'esecuzione. È ad esempio possibile utilizzare Return per uscire da una funzione o Exit per uscire da un ciclo. Nelle versioni precedenti a VB 2005 non era invece possibile passare all'iterazione successiva di un ciclo. La funzionalità mancante è stata aggiunta in VB 2005 sotto forma di una nuova istruzione Continue.

Continue è uno di quei dettagli del linguaggio che a prima vista può sembrare di secondaria importanza, ma che subito si rivela molto utile. Esistono tre versioni dell'istruzione Continue: Continue For, Continue Do e Continue While, ciascuna delle quali viene utilizzata con un diverso tipo di ciclo (For ... Next, Do ... Loop o While ... End While).

Per vedere come funziona l'istruzione Continue, esaminare il codice riportato di seguito:

For i = 1 to 1000
    If i Mod 5 = 0 Then
        ' (Task A code.)
        Continue For
    End If
    ' (Task B code.)
Next

Il codice esegue i cicli 1.000 volte, incrementando il contatore i. Ogni volta che il valore è divisibile per 5, viene eseguito il codice di Task A, quindi viene eseguita l'istruzione Continue For, il contatore viene incrementato e l'esecuzione riprende dall'inizio del ciclo, ignorando il codice di Task B.

In questo esempio l'istruzione Continue non è strettamente necessaria in quanto il codice può essere riscritto utilizzando una logica condizionale più sofisticata. Se, tuttavia, si ha a che fare con una sezione di codice particolarmente intricata, nella quale è necessario eseguire una serie di test diversi, è molto comodo utilizzare l'istruzione Continue per uscire rapidamente.

Sussiste però una limitazione: l'istruzione Continue potrebbe non funzionare come previsto in un ciclo nidificato. Se un ciclo è nidificato all'interno di un altro dello stesso tipo, non è possibile fare riferimento al ciclo più esterno in un modo che non sia ambiguo, pertanto l'istruzione Continue si riferisce sempre al ciclo interno.

Se i loop sono di tipo diverso non vi è tuttavia alcun problema. Nidificando ad esempio un ciclo For all'interno di un ciclo Do, è possibile utilizzare Continue For per ignorare la successiva iterazione del ciclo interno o Continue Do per ignorare la successiva iterazione del ciclo esterno, come illustrato di seguito:

Do
    For i = 1 to 1000
        If i Mod 5 = 0 Then
            ' (Task A code.)

            ' The next line skips Task B and Task C.
            Continue Do
        End If
        ' (Task B code.)
    Next
    ' (Task C code.)
Loop Until StopLoop

Questa tecnica funziona anche al contrario, ossia con il ciclo Do all'interno del ciclo For.

Istruzione Using

In .NET è di vitale importanza accertarsi che gli oggetti che utilizzano risorse non gestite (quali handle di file, connessioni a database, contesti grafici, e così via) le rilascino appena possibile. In quest'ottica gli oggetti devono sempre implementare l'interfaccia IDisposable e fornire un metodo Dispose() da chiamare per rilasciare immediatamente le risorse.

Il solo problema è che chi scrive codice deve sempre ricordarsi di chiamare il metodo Dispose() o un altro metodo che chiami Dispose(), ad esempio Close(). VB 2005 dispone di un nuovo strumento di salvaguardia di cui è possibile avvalersi per avere la certezza che venga sempre chiamato Dispose(): l'istruzione Using.

L'istruzione Using viene utilizzata in una struttura a blocchi. Nella prima riga, quando si dichiara il blocco Using, si specifica l'oggetto eliminabile utilizzato. Di solito l'oggetto viene creato contemporaneamente utilizzando la parola chiave New. Si scrive quindi il codice che utilizza l'oggetto eliminabile all'interno del blocco Using. Di seguito è riportato un esempio con uno spezzone di codice che crea un nuovo file e vi scrive dei dati:

Using NewFile As New _
  System.IO.StreamWriter("c:\MyFile.txt")
    NewFile.WriteLine("This is line 1")
    NewFile.WriteLine("This is line 2")
End Using

' The file is closed automatically. 
' The NewFile object is no longer available here.

Nell'esempio, non appena l'esecuzione oltrepassa il blocco Using, viene chiamato il metodo Dispose() sull'oggetto NewFile, rilasciando l'handle del file. Per fortuna in .NET viene eseguito un controllo per verificare che la risorsa venga eliminata a prescindere dalla modalità di uscita dal blocco Using, anche se si verifica un'eccezione non gestita.

Accessibilità di Split

In VB vengono riconosciuti tre livelli di accessibilità. Di seguito sono riportati nell'ordine dal più permissivo al più vincolante:

Supponiamo di creare un componente DLL che verrà utilizzato da un'altra applicazione e di decidere di creare una proprietà chiamata Status, che l'applicazione client dovrà leggere. Si attribuisce quindi Public alla proprietà:

Public Property Status() As Integer
    Get
        Return _Status
    End Get
    Set(ByVal value As Integer)
        _Status = value
    End Set
End Property

Nell'esempio sia la procedura set sia la procedura get della proprietà hanno la stessa accessibilità, che in VB .NET 1.0 è un requisito. Qui il problema risiede nel fatto che il client ha la facoltà di modificare la proprietà Status, il che non ha senso. È possibile rendere la proprietà di sola lettura o, in altre parole, omettere completamente la procedura set, ma così facendo le altre classi contenute nell'assembly del componente non potrebbero più modificare Status.

La soluzione offerta da VB 2005 consiste nell'attribuire il livello di accessibilità Friend alla procedura set della proprietà. Ecco come si presenta il codice:

Public Property Status() As Integer
    Get
        Return _Status
    End Get
    Friend Set(ByVal value As Integer)
        _Status = value
    End Set
End Property

Insiemi generici

Uno dei principi chiave della programmazione orientata agli oggetti è creare soluzioni che siano il più possibile astratte e generiche, in modo da poterle riutilizzare in situazioni diverse. Purtroppo non è sempre facile. Una delle sfide che i programmatori si trovano ad affrontare è creare classi che siano sufficientemente generiche da poter essere riutilizzate, ma nel contempo siano anche sufficiente specifiche da individuare gli errori e assicurare buone prestazioni.

Vediamo ad esempio gli insiemi. Un buon programmatore che utilizza tecnologie orientate agli oggetti preferisce creare una classe Collection generica, utilizzabile con qualsiasi tipo di oggetto, piuttosto che un insieme distinto per ogni tipo di elemento specifico (come OrderItemCollection). Chiaramente la classe Collection generica è più semplice, elegante e impone un carico lavoro molto minore rispetto a una serie di classi personalizzate come OrderItemCollection. Considerando tuttavia le prestazioni e la protezione dei tipi, la soluzione generica non risulta poi così vantaggiosa. In altre parole, se si utilizza una classe Collection generica per memorizzare oggetti OrderItem, non è possibile avere la certezza che altri non inseriscano altri tipi di oggetti, causando un problema insidioso in futuro.

.NET 2.0 offre una soluzione chiamata insiemi generici. Gli insiemi generici sono classi parametrizzate per tipo, vale a dire che si crea un modello di classe in grado di supportare tutti i tipi. Quando si crea un'istanza della classe, si specifica il tipo che si desidera utilizzare e da quel punto in poi l'oggetto è legato al tipo scelto. Gli insiemi generici sono incorporati in Common Language Runtime e sono quindi disponibili per tutti i linguaggi che li supportano, compresi VB e C#. Per ulteriori informazioni sull'implementazione degli insiemi generici in C#, vedere l'articolo di Joe Mayo sull'argomento in questo numero.

Un altro esempio in cui gli insiemi generici sono molto utili è la classe System.Collections.ArrayList. ArrayList è un insieme utilizzabile a tutti gli scopi, dimensionato dinamicamente. Può contenere normali oggetti .NET o oggetti personalizzati. Per fornire questo supporto, ArrayList tratta tutto come se si trattasse del tipo Object di base. Il problema sta nel fatto che non è possibile imporre limitazioni al funzionamento di ArrayList. Se si desidera, ad esempio, utilizzare ArrayList per memorizzare un insieme di oggetti Customer, non vi è modo di sapere se un pezzo di codice errato non vada ad inserire accidentalmente stringhe, valori integer o qualsiasi altro tipo di oggetto, causando notevoli problemi in futuro. Per questa ragione gli sviluppatori spesso creano classi di insiemi personalizzate, fortemente tipizzate. Di fatto la libreria di classi di .NET ne contiene a dozzine.

Gli insiemi generici contribuiscono a risolvere il problema, in quanto consentono, ad esempio, di dichiarare una classe che funzioni con qualunque tipo utilizzando la parola chiave Of:

Public Class GenericList(Of ItemType)
    ' (Code goes here)
End Class

In questo caso viene creata una nuova classe chiamata GenericList in grado di funzionare con qualsiasi tipo di oggetto. Il client deve tuttavia specificare quale sia il tipo da utilizzare. Nel codice della classe si farà riferimento a questo tipo come ItemType. Ovviamente ItemType non è effettivamente un tipo, si tratta piuttosto di un segnaposto del tipo che si sceglierà quando si creerà un oggetto GenericList. Nel Listato 2 è illustrato il codice completo di un ArrayList type-safe.

Listato 2. Insieme type-safe con insiemi generici

Public Class GenericList(Of ItemType)
    Inherits CollectionBase

    Public Function Add(ByVal value As ItemType) _
      As Integer
        Return List.Add(value)
    End Function

    Public Sub Remove(ByVal value As ItemType)
        List.Remove(value)
    End Sub

    Public ReadOnly Property Item( _
      ByVal index As Integer) As ItemType
        Get
            ' The appropriate item is retrieved from 
            ' the List object and explicitly cast to 
            ' the appropriate type, and then returned.
            Return CType(List.Item(index), ItemType)
        End Get
    End Property
End Class

La classe GenericList funge da wrapper per un normale ArrayList, ma fornisce i metodi fortemente tipizzati Add() e Remove(), che utilizzano il segnaposto ItemType.

Segue un esempio dell'utilizzo della classe GenericList per creare un insieme ArrayList che supporti solo le stringhe:

' Create the GenericList instance, and 
' choose a type (in this case, string)
Dim List As New GenericList(Of String)

' Add two strings.
List.Add("blue")
List.Add("green")

' The next statement will fail because it has the 
' wrong type. There is no automatic way to convert 
' a Guid object to a string. In fact, this line 
' won't ever run, because the compiler notices the 
' problem and refuses to build the application.
List.Add(Guid.NewGuid())

Non esistono limiti per le modalità di parametrizzazione di una classe. Nell'esempio di GenericList è presente un solo parametro di tipo. Non è comunque affatto difficile creare una classe che funzioni con due o più tipi di oggetti e consenta di renderli generici. Per utilizzare questo approccio, separare i tipi mediante una virgola, da posizionare fra le parentesi angolari all'inizio di una classe.

Esaminare, ad esempio, la classe GenericHashTable riportata di seguito: consente di definire il tipo che si desidera utilizzare per gli elementi da memorizzare (ItemType) e le chiavi utilizzate per indicizzare i tipi (KeyType).

Public Class GenericHashTable(Of ItemType, KeyType)
    Inherits DictionaryBase
    ' (Code goes here.)
End Class

Un'altra funzionalità importante degli insiemi generici è la capacità di utilizzare i vincoli per limitare i tipi consentiti. Supponiamo, ad esempio, di creare una classe che supporti tutti i tipi che soddisfano una determinata interfaccia. In questo caso, dopo avere dichiarato il segnaposto del tipo, si aggiunge la parola chiave As seguita dalla classe di base o interfaccia necessaria per il tipo.

Segue un esempio in cui la classe GenericList è stata limitata in modo da utilizzare solo elementi serializzabili. Una buona ragione per utilizzare questo approccio potrebbe essere il desiderio di aggiungere a GenericList un altro metodo che richiede la serializzazione, ad esempio un metodo che scrive tutti gli elementi in un flusso.

Public Class GenericList(Of ItemType As ISerializable)
    Inherits CollectionBase 
    ' (Code goes here.)
End Class

È possibile definire tutti i vincoli desiderati, purché ogni classe o interfaccia venga separata mediante il simbolo &. I vincoli vengono applicati dal compilatore.

I progettisti di .NET Framework conoscono bene l'utilità degli insiemi generici e ne hanno già creati alcuni che tutti gli sviluppatori possono utilizzare. Sono disponibili nel nuovo spazio dei nomi Systems.Collections.Generic. Comprendono:

La maggior parte di questi tipi sono duplicati dei tipi presenti nello spazio dei nomi System.Collections. Gli insiemi precedenti vengono mantenuti per garantire la compatibilità a ritroso.

Conclusioni

Come si deduce leggendo l'articolo, le modifiche del linguaggio apportate nell'ultima versione di VB sono perfezionamenti che semplificano il lavoro degli sviluppatori senza rendere obsoleto il codice già esistente. Molte di queste modifiche sono funzionalità minori del linguaggio importate da C# (come l'overload degli operatori), mentre altre sono funzioni completamente nuove incorporate nella versione più recente di Common Language Runtime (come gli insiemi generici). Ne emerge che il linguaggio VB è tutt'altro che superato e la continua evoluzione non fa che migliorarlo.

Per ulteriori informazioni su Hardcore Visual Studio e Pinnacle Publishing, visitare il sito Web all'indirizzo http://www.pinpub.com/ (in inglese)

Nota: questo non è un sito Web Microsoft Corporation. Microsoft non è in alcun modo responsabile del suo contenuto.

Questo articolo è riprodotto dal numero di novembre 2004 di Hardcore Visual Studio. Copyright 2004, by Pinnacle Publishing, Inc., se non specificato altrimenti. Tutti i diritti riservati. Hardcore Visual Studio è una pubblicazione indipendente di Pinnacle Publishing, Inc. Nessuna parte di questo articolo può essere utilizzata o riprodotta in qualsiasi formato (tranne che in citazioni utilizzate in articoli e recensioni) senza il consenso preventivo di Pinnacle Publishing, Inc. Per contattare Pinnacle Publishing, Inc., chiamare il numero 1-800-788-1900.

 

© 2005 Microsoft Corporation. Tutti i diritti riservati. Note legali.