Condividi tramite


Visual Basic 2010

Novità di Visual Basic 2010

Jonathan Aneja

Fin dalle sue origini nel 1991, il linguaggio Visual Basic è sempre stato uno straordinario strumento di produttività per la compilazione di applicazioni. Pressoché 20 anni dopo, continua a fornire un agevole accesso a Microsoft .NET Framework, consentendo agli sviluppatori di scrivere applicazioni per desktop, telefoni, browser e cloud.

Questo mese verrà rilasciato Microsoft Visual Studio 2010 che incorpora la versione 10 di Visual Basic (a volte indicata come VB 2010 o VB10). Questa versione, la più potente, contiene numerose funzionalità che consentono agli sviluppatori di risparmiare tempo e di ottenere di più con un minor numero di righe di codice. Di seguito vengono fornite informazioni esaurienti relative all'utilizzo di Visual Basic in Visual Studio 2010.

Coevoluzione

In passato, Visual Basic e C# venivano sviluppati da team separati e spesso alcune funzionalità apparivano prima in un linguaggio e successivamente nell'altro. C# disponeva, ad esempio, di inizializzatori di insieme e di proprietà implementate automaticamente che non erano presenti in Visual Basic e Visual Basic disponeva di funzionalità come l'associazione tardiva e i parametri facoltativi che non erano presenti in C#. Ma ogni volta che veniva inserita una funzionalità in uno dei linguaggi, molti clienti chiedevano che venisse aggiunta anche all'altro.

Per risolvere questa situazione, Microsoft decise di unire i team di Visual Basic e C# e adottare una strategia di coevoluzione. L'obiettivo era far evolvere in parallelo i due linguaggi. Quando una funzionalità importante viene introdotta in un linguaggio, lo deve essere anche nell'altro. Ciò non significa che ogni funzionalità sarà presente in entrambi i linguaggi e che funzioneranno esattamente allo stesso modo. Ogni linguaggio ha di fatto la sua storia, il suo spirito e caratteristiche distinte che è importante preservare. Con coevoluzione si intende specificare che qualsiasi attività sia possibile svolgere in un linguaggio lo deve essere anche nell'altro.

In .NET Framework 4, sia Visual Basic sia C# hanno fatto passi da gigante verso questo obiettivo, ciascuno aggiungendo diverse funzionalità che l'altro linguaggio già presentava. La coevoluzione non riguarda solo il passato, è anche la strategia scelta per l'innovazione futura dei due linguaggi. Con questo obiettivo, .NET Framework 4 introduce nuove e potenti funzionalità, ad esempio Dynamic Language Runtime, Incorpora tipi di interoperabilità e la varianza generica, contemporaneamente in entrambi i linguaggi, consentendo agli sviluppatori Visual Basic e C# di sfruttare tutti i vantaggi di .NET Framework.

Nuove funzionalità di Visual Basic 2010

Le nuove funzionalità di Visual Basic 2010 sono progettate per eseguire un maggior numero di operazioni in un minor numero di righe di codice. Il team di progettazione di Visual Basic ha analizzato le operazioni per le quali gli sviluppatori devono scrivere una grande quantità di noioso codice standard e ha individuato modi per far sì che tali operazioni vengano eseguite automaticamente dal compilatore. Finora è stato illustrato il quadro generale, di seguito verranno analizzate in dettaglio alcune funzionalità.

Continuazione di riga implicita

Visual Basic è un linguaggio orientato alla riga che utilizza una sintassi chiara e simile all'inglese per migliorare la leggibilità. Tuttavia, questo comporta il raggiungimento del limite di 80 caratteri per riga che obbliga gli sviluppatori ad andare a capo spesso. È possibile utilizzare il carattere di sottolineatura per indicare al compilatore che l'elaborazione deve continuare alla riga successiva, ovvero che deve considerare più righe fisiche come una sola riga logica. Purtroppo, il dover digitare ripetutamente uno stesso carattere di sottolineatura è alquanto irritante e infatti per anni la prima richiesta degli sviluppatori è stata quella di una funzionalità del compilatore che rendesse automatica l'operazione.

In Visual Basic 2010 questo è possibile. Il compilatore sa quali token (virgole, parentesi e operatori) sono presenti con maggiore frequenza subito prima del carattere di continuazione di riga, quindi inserisce il carattere sollevando gli sviluppatori da questa azione. Ad esempio, il compilatore sa che terminare un'istruzione di Visual Basic con una virgola non è un'operazione valida, quindi quando incontra un flusso di token simile a {virgola, invio}, deduce la presenza del carattere di continuazione di riga, come viene illustrato nell'esempio riportato nella Figura 1.

Figura 1 Deduzione della continuazione di riga

<Extension()>
Function FilterByCountry(
  ByVal customers As IEnumerable(Of Customer),
  ByVal country As String) As IEnumerable(Of Customer)
    Dim query =
      From c In customers
      Where c.Country = country
      Select <Customer>
        <%=
          c.Name &
          "," &
          c.Country
        %>
      </Customer>
    Return query
  End Function

In Visual Basic 2008 nel codice riportato nella Figura 1 sarebbero stati necessari nove caratteri di sottolineatura. In ciascuno di questi casi, però, il compilatore ha dedotto i casi in cui la sottolineatura era necessaria e ne ha consentito l'omissione:

  • Dopo l'attributo <Extension()>
  • Dopo la parentesi di apertura ( ( ) nella dichiarazione del metodo
  • Dopo la virgola (,) per il primo parametro
  • Prima della parentesi di chiusura ( ) ) nella dichiarazione del metodo
  • Dopo il segno di uguale (=)
  • Dopo il tag di apertura per un'espressione incorporata (<%=)
  • Dopo ogni e commerciale (&) nel valore letterale XML
  • Prima del tag di chiusura per un'espressione incorporata (%>)

Questa nuova funzionalità del compilatore è particolarmente utile per la firma del metodo che andrebbe ben oltre gli 80 caratteri dell'esempio illustrato se ciascuna parte fosse sulla stessa riga. Nella Figura 2 verranno illustrate tutte le combinazioni di token e posizioni in cui il carattere di continuazione di riga è implicito.

Figura 2 Casi in cui i caratteri di continuazione sono impliciti

Token Prima Dopo
, (virgola),   .  (punto),   >  (attributi),   (  {  (parentesi di apertura),   <%=  (apertura di un'espressione incorporata (valori letterali XML))   X
),   },   ,   ]  (parentesi di chiusura), %> (chiusura di un'espressione incorporata) X  

Tutte le parole chiave LINQ:

Aggregate, Distinct, From, Group By, Group Join, Join, Let, Order By, Select, Skip, Skip While, Take, Take While, Where, In, Into, On, Ascending, Descending

X X

Operatori:

+ ,   - ,   * ,   / ,   \ ,   ^ ,   >> ,   << ,   Mod,   & ,   += ,   -= ,   *= ,   /= ,   \= ,   ^= ,   >>= ,   <<= ,   &= ,   < ,   <= ,   > ,   >= ,   <> ,   Is,  IsNot,  Like,  And,   Or,  Xor,  AndAlso,  OrElse

  X
With (in un inizializzatore di oggetto)   X

Come si può osservare, esistono più di 60 posizioni in cui il linguaggio non richiede caratteri di sottolineatura. Di fatto, in nessuno degli esempi di codice di questo articolo è stato necessario il carattere di continuazione di riga. Naturalmente, è ancora possibile utilizzare il carattere di sottolineatura e il codice di versioni precedenti di Visual Basic verrà ancora compilato come previsto.

Lambda di istruzioni

Il termine lambda può risultare incomprensibile, ma una lambda è semplicemente una funzione definita all'interno di un'altra funzione. In Visual Basic 2008 sono state introdotte le espressioni lambda con la parola chiave Function:

Dim customers As Customer() = ...

 Array.FindAll(customers, Function(c) c.Country = "Canada")

Le espressioni Lambda costituiscono un modo compatto di esprimere la logica in modalità locale senza doverla suddividere tra più metodi. Di seguito viene illustrato come il codice precedente sarebbe stato scritto in Visual Basic 2005 (che non supportava le espressioni Lambda):

Dim query = Array.FindAll(customers, AddressOf Filter)

    ...

Function Filter(ByVal c As customer) As Boolean
  Return c.Country = "Canada"
End Function

Sfortunatamente, le espressioni Lambda di Visual Basic 2008 richiedevano la restituzione di un valore, pertanto:

Array.ForEach(customers, Function(c) Console.WriteLine(c.Country))

avrebbe causato:

'Compile error: "Expression does not produce a value."

Console.WriteLine è una subroutine (void, in C#), quindi non restituisce un valore e questo è il motivo per cui il compilatore genera un errore. Per risolvere questo problema, in Visual Basic 2010 è stato introdotto il supporto per le funzioni Lambda di istruzioni, ovvero lambda contenenti una o più istruzioni:

Array.ForEach(customers, Sub(c) Console.WriteLine(c.Country))

Poiché Console.WriteLine non restituisce un valore, è possibile creare una funzione lambda Sub anziché una funzione Lambda Function. Di seguito un altro esempio in cui vengono utilizzate più istruzioni:

Array.ForEach(customers, Sub(c)
                           Console.WriteLine("Country Name:")
                           Console.WriteLine(c.Country)
                         End Sub)

Quando viene eseguito questo codice, vengono stampate due righe per ciascun cliente. Inoltre, se si posiziona il puntatore del mouse su c durante la scrittura di codice, si noterà che il compilatore ha dedotto il tipo come Customer (è anche corretto digitare c As Customer per dichiarare il tipo in modo esplicito). Il collegamento dinamico dei gestori eventi rappresenta un'altra modalità di utilizzo delle funzioni Lambda di istruzioni:

AddHandler b.Click, Sub(sender As Object, e As EventArgs)
                      MsgBox("Button Clicked")
                      'insert more complex logic here
                    End Sub

È infatti possibile combinare le funzioni Lambda di istruzioni con una funzionalità introdotta in Visual Basic 2008: delegati di tipo "relaxed". È possibile utilizzare i delegati, puntatori alle funzioni indipendenti dai tipi, per eseguire contemporaneamente più funzioni. Questa combinazione genera una firma ancora più semplice:

AddHandler b.Click, Sub()
                      MsgBox("Button Clicked")
                     'insert more complex logic here
                    End Sub

I delegati di tipo relaxed consentono di omettere completamente i parametri da un gestore eventi, un vantaggio interessante visto che spesso non vengono utilizzati e non sono altro che ulteriori elementi di disturbo.

Oltre alle funzioni lambda Sub su una o più righe descritte finora, in Visual Basic 2010 sono supportate anche le funzioni Lambda Function su più righe:

Dim query = customers.Where(Function(c)
                              'Return only customers that have not been saved
                              'insert more complex logic here
                              Return c.ID = -1
                            End Function)

Un altro aspetto interessante delle funzioni Lambda di istruzioni è il modo in cui si intersecano con i delegati anonimi introdotti in Visual Basic 2008. Spesso questi ultimi vengono confusi con i metodi anonimi di C#, sebbene tecnicamente non siano la stessa cosa. Si ottengono delegati anonimi quando il compilatore di Visual Basic deduce un tipo delegato in base alla firma di metodo di una funzione Lambda:

Dim method = Function(product As String)
               If product = "Paper" Then
                 Return 4.5 'units in stock
               Else
                 Return 10 '10 of everything else
               End If
             End Function

MsgBox(method("Paper"))

Se viene eseguito questo codice, nella finestra di messaggio verrà visualizzato il valore 4.5. Inoltre, se si posiziona il puntatore del mouse su method, verrà visualizzato il testo Dim method As <Function(String) As Double>. Poiché non è stato fornito alcun tipo delegato effettivo, il compilatore ne genererà uno automaticamente come il seguente:

Delegate Function $compilerGeneratedName$(product As String) As Double

Si tratta di un delegato anonimo perché viene visualizzato solo nel codice prodotto dal compilatore e non in quello scritto. Si noti che il compilatore ha dedotto il tipo restituito come Double, quando di fatto non era presente alcuna clausola As per specificare il tipo restituito della funzione Lambda. Il compilatore analizza tutte le istruzioni return nella funzione Lambda e trova i tipi Double (4.5) e Integer (10):

'Notice the "As Single"
Dim method = Function(product As String) As Single
               If product = "Paper" Then
                 Return 4.5 'units in stock
               Else
                 Return 10 '10 of everything else
               End If
             End Function

Esegue quindi l'algoritmo di tipo dominante e determina che può convertire in modo sicuro 10 in Double, ma non 4.5 in Integer; pertanto Double è la scelta migliore.

È inoltre possibile assumere il controllo esplicito del tipo restituito, nel qual caso il compilatore non tenterà di dedurre il tipo. Anziché affidarsi al compilatore per la deduzione del tipo delegato, è molto comune assegnare una funzione Lambda a una variabile con un tipo delegato esplicito:

Dim method As Func(Of String, Single) =
  Function(product)
    If product = "Paper" Then
      Return 4.5 'units in stock
    Else
      Return 10 '10 of everything else
    End If
  End Function

Poiché è stato fornito un tipo di destinazione esplicito, non è necessario specificare As String o As Single. Il compilatore è in grado di dedurne la presenza in base al tipo delegato sul lato sinistro dell'istruzione. Pertanto, se si posiziona il puntatore del mouse su product si noterà che il tipo dedotto è String. Non è più necessario specificare As Single perché il tipo delegato fornisce già questa informazione. Nell'esempio precedente, la firma del delegato Func (inclusa in .NET Framework) è simile a quanto segue:

Delegate Function Func(Of T, R)(ByVal param As T) As R

con un'eccezione minore, come si vedrà in seguito nella sezione Varianza generica.

Proprietà implementate automaticamente

In Visual Basic le proprietà sono membri di classi utilizzati per esporre lo stato di un oggetto al mondo esterno. Una tipica dichiarazione di proprietà avrà un aspetto simile a quanto segue:

Private _Country As String

Property Country As String
  Get
    Return _Country
  End Get
  Set(ByVal value As String)
    _Country = value
  End Set
End Property

10 righe di codice per un concetto in realtà molto semplice. Poiché gli oggetti tipici dispongono spesso di decine di proprietà, si finisce per includere una notevole quantità di codice standard nelle definizioni di classe. Per semplificare tali attività, in Visual Basic 2010 sono state introdotte le proprietà implementate automaticamente che consentono di definire una proprietà semplice utilizzando una sola riga di codice:

Property Country As String

In questo caso, il compilatore genererà automaticamente campi sottostanti, di richiamo e impostazione. Il nome del campo sottostante sarà sempre un carattere di sottolineatura seguito dal nome della proprietà: _Country in questo caso. Questa convenzione di denominazione garantisce la compatibilità della serializzazione binaria se una proprietà implementata automaticamente dovesse essere modificata in una normale. La serializzazione binaria continuerà a funzionare, a condizione che il campo sottostante sia lo stesso.

Una delle operazioni più interessanti che si possa eseguire con le proprietà implementate automaticamente è specificare gli inizializzatori che impostano il valore predefinito della proprietà durante l'esecuzione del costruttore. In uno scenario comune con classi di entità, ad esempio, la chiave primaria viene impostata su un valore come -1 per indicarne lo stato non salvato. Ecco come dovrebbe risultare il codice:

Property ID As Integer = -1

Quando il costruttore è in esecuzione, il campo sottostante (_ID) verrà impostato automaticamente sul valore -1. La sintassi dell'inizializzatore viene utilizzata anche per i tipi di riferimento:

Property OrderList As List(Of Order) = New List(Of Order)

La riga di codice precedente potrebbe non risultare in vero stile Visual Basic dato che la doppia immissione del nome del tipo è ridondante. La buona notizia è che è disponibile una sintassi ancora più breve coerente con ciò che Visual Basic consente nelle normali dichiarazioni di variabile:

Property OrderList As New List(Of Order)

È inoltre possibile combinarla con gli inizializzatori di oggetti per consentire l'impostazione di proprietà aggiuntive:

Property OrderList As New List(Of Order) With {.Capacity = 100}

Ovviamente, per le proprietà più complesse è ancora necessario ricorrere alla sintassi estesa. È ancora possibile digitare Property{Tab} per attivare il frammento della proprietà precedente. In alternativa, dopo avere digitato la prima riga della proprietà, è possibile immettere semplicemente Get{Enter} e l'IDE genererà la proprietà nello stile precedente:

Property Name As String
  Get

  End Get
  Set(ByVal value As String)

  End Set
End Property

Spesso viene segnalato che la nuova sintassi di proprietà è quasi identica alla sintassi di un campo pubblico, quindi perché non utilizzare un campo pubblico? Per alcuni motivi:

  • Gran parte dell'infrastruttura di associazione dati .NET funziona per le proprietà, ma non per i campi.
  • Un'interfaccia non può imporre l'esistenza di un campo, ma può imporre quella di una proprietà.
  • Le proprietà forniscono una flessibilità più a lungo termine per la modifica delle regole di business. Si supponga ad esempio che venga introdotta la regola per cui un numero di telefono deve essere composto da 10 cifre. Non vi è modo di eseguire questa convalida con l'assegnazione a un campo pubblico. La modifica di un campo pubblico in una proprietà è una modifica importante per scenari quali serializzazione binaria e reflection.

Inizializzatori di insieme

Una pratica .NET comune consiste nella creazione di un'istanza di un insieme e nel relativo popolamento chiamando il metodo Add una volta per ciascun elemento:

Dim digits As New List(Of Integer)
digits.Add(0)
digits.Add(1)
digits.Add(2)
digits.Add(3)
digits.Add(4)
digits.Add(5)
digits.Add(6)
digits.Add(7)
digits.Add(8)
digits.Add(9)

Il risultato è purtroppo un notevole sovraccarico sintattico per un concetto fondamentalmente molto semplice. In Visual Basic 2010 sono stati introdotti inizializzatori di insieme per semplificare la creazione di istanze di insiemi. Con questo codice:

Dim digits = New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}

il compilatore genererà automaticamente tutte le chiamate al metodo Add. È anche possibile utilizzare la funzionalità con la sintassi As New di Visual Basic:

Dim digits As New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}

Si noti che nel team di Visual Basic si consiglia sempre di utilizzare la seconda sintassi (As New) rispetto alla prima perché rende flessibile il codice rispetto alle modifiche apportate all'impostazione Option Infer.

È possibile utilizzare gli inizializzatori di insieme per qualsiasi tipo che soddisfi i requisiti seguenti:

  • È possibile scorrerlo utilizzando un'istruzione For Each, ovvero implementa IEnumerable. Per una definizione più precisa/dettagliata di un tipo di insieme, vedere la sezione 10.9.3 relativa a Specifiche del linguaggio Visual Basic all'indirizzo msdn.microsoft.com/library/aa711986(VS.71).aspx.
  • Presenta un costruttore senza parametri accessibile (non necessariamente pubblico).
  • Presenta un'istanza accessibile (non necessariamente pubblica) o un metodo di estensione denominato Add.

Ciò significa che è anche possibile utilizzare gli inizializzatori di insieme con tipi più complessi, ad esempio i dizionari:

Dim lookupTable As New Dictionary(Of Integer, String) From
  {{1, "One"},
   {2, "Two"},
   {3, "Three"},
   {4, "Four"}}

Si noti che anche se questa istruzione si estende su cinque righe, non sono presenti caratteri di sottolineatura. In questo caso, il compilatore genererà codice equivalente al modo precedente di inizializzare il dizionario:

Dim lookupTable As New Dictionary(Of Integer, String)
lookupTable.Add(1, "One")
lookupTable.Add(2, "Two")
lookupTable.Add(3, "Three")
lookupTable.Add(4, "Four")

Il compilatore chiama un metodo Add con due parametri anziché uno. È in grado di eseguire questa operazione perché i valori passati nell'inizializzatore di insieme erano racchiusi tra parentesi graffe annidate, come riportato di seguito: {{1, "Uno"}, {2, "Due"}, …}. Per ogni set di parentesi graffe annidate, il compilatore tenta di passare tali parametri a un metodo Add compatibile.

È inoltre possibile fornire un'implementazione Add personalizzata tramite un metodo di estensione:

<Extension()>
  Sub Add(ByVal source As IList(Of Customer),
          ByVal id As Integer,
          ByVal name As String,
          ByVal city As String)

      source.Add(New Customer With
                 {
                    .ID = id,
                    .Name = name,
                    .City = city
                 })
  End Sub

(Si noti la mancanza di tutti quei caratteri di sottolineatura) Questo metodo estende qualsiasi tipo che implementi IList(Of Customer), quindi consente di utilizzare la nuova sintassi dell'inizializzatore di insieme come segue:

Dim list = New List(Of Customer) From
            {
              {1, "Jon", "Redmond"},
              {2, "Bob", "Seattle"},
              {3, "Sally", "Toronto"}
            }

(con l'aggiunta di tre clienti a list). È inoltre possibile utilizzare inizializzatori di insieme con proprietà implementate automaticamente:

Property States As New List(Of String) From {"AL", "AK", "AR", "AZ", ...}

Valori letterali di matrice

Oltre a consentire utilizzi più efficaci dei tipi di insieme, Visual Basic 2010 offre alcuni importanti miglioramenti relativi all'utilizzo delle matrici. Si consideri il codice seguente (che funziona bene nelle versioni precedenti):

Dim numbers As Integer() = New Integer() {1, 2, 3, 4, 5}

Dall'analisi degli elementi della matrice emerge chiaramente che ciascuno di essi è un Integer, quindi l'effettiva doppia immissione di Integer in questa riga non aggiunge in realtà alcun valore. I valori letterali di matrice consentono la creazione di una matrice tramite l'inserimento di tutti gli elementi tra parentesi graffe e lasciando che sia il compilatore a dedurre il tipo:

Dim numbers = {1, 2, 3, 4, 5}

Il tipo dei numeri non è Object, ma piuttosto Integer() (purché l'opzione Option Infer sia attiva) perché il valore letterale della matrice è ora autonomo e dispone di un proprio tipo. Si consideri ora un esempio più complicato:

Dim numbers = {1, 2, 3, 4, 5.555}

In questo caso, il tipo dei numeri sarà dedotto come Double(). Il compilatore determina il tipo esaminando ogni elemento della matrice e calcolando il tipo dominante (tramite l'utilizzo dello stesso algoritmo illustrato in precedenza per la deduzione del tipo restituito di una funzione Lambda di istruzioni). Cosa accade se non è presente un tipo dominante, come nel codice seguente:

Dim numbers = {1, 2, 3, 4, "5"}

In questo caso, la conversione di un valore Integer in un valore String sarebbe una conversione verso un tipo di dati più piccolo e, analogamente, anche la conversione di un valore String in valore Integer sarebbe una conversione dello stesso genere. L'unico tipo sicuro da scegliere è Object() (il compilatore genererà un errore quando l'opzione Option Strict è attiva).

I valori letterali di matrice possono essere annidati per formare matrici multidimensionali o matrici di matrici:

'2-dimensional array
Dim matrix = {{1, 0}, {0, 1}} 

'jagged array - the parentheses force evaluation of the inner array first
Dim jagged = { ({1, 0}), ({0, 1}) }

Dynamic Language Runtime

Anche se tecnicamente può essere definito un linguaggio statico, Visual Basic ha sempre offerto funzionalità dinamiche estremamente efficaci come l'associazione tardiva. Visual Studio 2010 viene fornito con una nuova piattaforma chiamata Dynamic Language Runtime (DLR), che semplifica la creazione e la comunicazione tra linguaggi dinamici. Visual Basic 2010 è stato aggiornato per supportare completamente DLR nell'associazione tardiva, consentendo agli sviluppatori di utilizzare librerie e framework sviluppati in altri linguaggi, ad esempio IronPython/IronRuby.

L'aspetto interessante di questa funzionalità è che nulla viene modificato sintatticamente (di fatto non è stata modificata una sola riga di codice nel compilatore per supportare questa funzionalità). Gli sviluppatori possono eseguire operazioni con associazione tardiva esattamente come nelle versioni precedenti di Visual Basic. Ciò che è stato modificato è il codice nel runtime di Visual Basic (Microsoft.VisualBasic.dll) che ora riconosce l'interfaccia IDynamicMetaObjectProvider fornita da DLR. Se un oggetto implementa questa interfaccia, il runtime di Visual Basic creerà un sito di chiamata DLR e consentirà all'oggetto e al relativo linguaggio di inserire la propria semantica nell'operazione.

Ad esempio, le librerie standard di Python contengono un file denominato random.py con un metodo denominato shuffle che può essere utilizzato per ridisporre in modo casuale gli elementi di una matrice. La chiamata a tale metodo è semplice:

Dim python As ScriptRuntime = Python.CreateRuntime()
Dim random As Object = python.UseFile("random.py")

Dim items = {1, 2, 3, 4, 5, 6, 7}
random.shuffle(items)

In fase di esecuzione, Visual Basic rileva che l'oggetto implementa IDynamicMetaObjectProvider, pertanto passa il controllo a DLR che comunica con Python ed esegue il metodo (passando la matrice definita in Visual Basic come argomento al metodo).

Si tratta di un esempio di chiamata di un'API predisposta per DLR, ma gli sviluppatori possono anche creare API personalizzate che utilizzano questa funzionalità. La cosa fondamentale è implementare l'interfaccia IDynamicMetaObjectProvider, nel qual caso i compilatori di Visual Basic e C# riconosceranno che l'oggetto dispone di una speciale semantica dinamica. Invece di un'implementazione manuale dell'interfaccia, è più semplice ereditare dalla classe System.Dynamic.DynamicObject (che implementa già questa interfaccia) ed eseguire l'override di un paio di metodi. Nella Figura 3 viene illustrato un esempio completo di creazione di un oggetto dinamico personalizzato (un "contenitore di proprietà" che sembra creare proprietà in modo istantaneo) e di chiamata tramite l'associazione tardiva normale di Visual Basic. Per ulteriori informazioni sull'utilizzo di DynamicObject, consultare l'eccellente articolo di Doug Rothaus all'indirizzo blogs.msdn.com/vbteam/archive/2010/01/20/fun-with-dynamic-objects-doug-rothaus.aspx.

Figura 3 Creazione di un oggetto dinamico personalizzato e relativa chiamata con l'associazione tardiva di Visual Basic

Imports System.Dynamic
  Module Module1
    Sub Main()
      Dim p As Object = New PropertyBag
        p.One = 1
        p.Two = 2
        p.Three = 3
      Console.WriteLine(p.One)
      Console.WriteLine(p.Two)
      Console.WriteLine(p.Three)
    End Sub
      Class PropertyBag : Inherits DynamicObject
        Private values As New Dictionary(Of String, Integer)
        Public Overrides Function TrySetMember(
          ByVal binder As SetMemberBinder,
          ByVal value As Object) As Boolean
            values(binder.Name) = value
          Return True
        End Function
        Public Overrides Function TryGetMember(
          ByVal binder As GetMemberBinder,
          ByRef result As Object) As Boolean
          Return values.TryGetValue(binder.Name, result)
        End Function
      End Class
  End Module

Varianza generica

Si tratta di è una funzionalità che può apparire veramente complessa (con termini come covarianza e controvarianza), ma in realtà è abbastanza semplice. Se si dispone di un oggetto di tipo IEnumerable(Of Apple) e si desidera assegnarlo a IEnumerable(Of Fruit), ciò dovrebbe essere consentito perché ogni Apple è un Fruit (imposto da una relazione di ereditarietà). Sfortunatamente, prima di Visual Basic 2010 la varianza generica non era supportata nel compilatore, anche se in realtà era supportata in Common Language Runtime (CLR).

Si prenda in considerazione l'esempio illustrato nella Figura 4. In Visual Basic 2008 il codice della Figura 4 genererebbe un errore di compilazione (o un'eccezione in fase di esecuzione se l'opzione Option Strict è disattivata) nella riga Dim enabledOnly. La soluzione consisteva nel chiamare il metodo di estensione .Cast, come illustrato di seguito:

'Old way, the call to Cast(Of Control) is no longer necessary in VB 2010
    Dim enabledOnly = FilterEnabledOnly(buttons.Cast(Of Control))

Questo non è più necessario perché in Visual Basic 2010 l'interfaccia IEnumerable è stata contrassegnata come Covariant mediante il modificatore Out:

Interface IEnumerable(Of Out T)
  ...
End Interface

Figura 4 Esempio di varianza generica

Option Strict On
Public Class Form1
  Sub Form1_Load() Handles MyBase.Load
    Dim buttons As New List(Of Button) From
      {
        New Button With
        {
          .Name = "btnOk",
          .Enabled = True
        },
        New Button With
        {
          .Name = "btnCancel",
          .Enabled = False
        }
      }

    Dim enabledOnly = FilterEnabledOnly(buttons)
  End Sub
  Function FilterEnabledOnly(
    ByVal controls As IEnumerable(Of Control)
    ) As IEnumerable(Of Control)
    Return From c In controls
    Where c.Enabled = True
  End Function
End Class

Ciò significa che il parametro generico T è ora Variant, ovvero viene utilizzato per le relazioni di ereditarietà, e il compilatore garantirà che venga utilizzato solo nelle posizioni in cui il tipo proviene dall'interfaccia. I parametri generici possono inoltre essere controvarianti, ovvero vengono utilizzati solo nelle posizioni di input. Un tipo può in realtà disporre di entrambi. Ad esempio, il delegato Func descritto in precedenza dispone sia di parametri controvarianti (elementi passati) sia di un parametro Covariant (per il tipo restituito):

Delegate Function Func(Of In T, Out R)(ByVal param As T) As R

È possibile utilizzare i modificatori In e Out nelle interfacce e nei delegati personalizzati. Molte interfacce e molti delegati di uso comune in .NET Framework 4 sono già stati contrassegnati come Variant; ne sono esempi comuni tutti i delegati Action/Func, IEnumerable(Of T), IComparer(Of T) e IQueryable(Of T).

L'aspetto interessante della varianza generica è che si tratta di una funzionalità di cui non bisogna preoccuparsi: purché funzioni correttamente, l'utente non se ne curerà mai. Le situazioni che causavano errori di compilazione o richiedevano una chiamata a .Cast (Of T) dovrebbero funzionare correttamente in Visual Basic 2010.

Parametri facoltativi migliorati

I parametri facoltativi forniscono una funzionalità di produttività utile che consente agli sviluppatori di creare metodi più flessibili ed evitare di contaminare una classe con numerosi overload di un metodo. In passato, un limite era dato dal fatto che i parametri facoltativi non potevano essere nullable (o qualsiasi tipo di struttura non intrinseco). Visual Basic 2010 consente ora di definire parametri facoltativi di qualsiasi tipo di valore:

Sub DisplayOrder(ByVal customer As Customer,
                 ByVal orderID As Integer,
                 Optional ByVal units As Integer? = 0,
                 Optional ByVal backgroundColor As Color = Nothing)
End Sub

In questo caso, le unità sono di tipo Nullable(Of Integer) e backgroundColor è un tipo di struttura non intrinseco, ma possono tuttavia essere utilizzati come parametri facoltativi. Visual Basic 2010 fornisce inoltre un supporto migliore per i parametri facoltativi generici.

Incorpora tipi di interoperabilità

Per le applicazioni che eseguono l'interoperabilità COM, l'utilizzo di Assembly di interoperabilità primari (PIA, Primary Interop Assemblies) rappresenta un problema comune. Un PIA è un assembly .NET che funge da Runtime Callable Wrapper (RCW) su un componente COM che dispone di un GUID univoco per l'identificazione. .Gli assembly .NET comunicano con un PIA che a sua volta esegue il marshalling necessario per lo spostamento dei dati tra COM e .NET.

Sfortunatamente, i PIA possono complicare la distribuzione perché sono DLL aggiuntive che devono essere distribuite nei computer degli utenti finali. Causano inoltre problemi al controllo delle versioni. Ad esempio, se si desidera che un'applicazione funzioni sia con Excel 2003 sia con Excel 2007, sarebbe necessario distribuire entrambi i PIA con l'applicazione.

I tipi di interoperabilità vengono incorporati direttamente nell'applicazione, ma solo i tipi e i membri del PIA assolutamente necessari, rimuovendo così la necessità di distribuire PIA nei computer degli utenti finali.

Per attivare questa funzionalità per un progetto esistente (è già attivata per impostazione predefinita per i nuovi riferimenti), selezionare il riferimento in Esplora soluzioni e modificare l'opzione Incorpora tipi di interoperabilità nella finestra delle proprietà (Figura 5). In alternativa, se la compilazione viene eseguita tramite il compilatore della riga di comando, utilizzare l'opzione /l (o /link) anziché /r e /reference.

immagine: Attivazione della funzionalità Incorpora tipi di interoperabilità in Esplora soluzioni

Figura 5 Attivazione della funzionalità Incorpora tipi di interoperabilità in Esplora soluzioni

Dopo avere attivato questa funzionalità, l'applicazione non presenta più una dipendenza da PIA. Di fatto, se si apre l'assembly in Reflector o ildasm, si noterà che in realtà non vi è assolutamente alcun riferimento a PIA.

Multitargeting

L'aspetto più interessante di tutte le funzionalità di Visual Basic 2010 è la possibilità di utilizzarle anche in progetti destinati a .NET Framework 2.0 fino a .NET Framework 3.5. Ciò significa che il carattere di continuazione riga implicito, i valori letterali della matrice, gli inizializzatori di insieme, le funzioni Lambda di istruzioni, le proprietà implementate automaticamente e così via funzioneranno nei progetti esistenti senza dover cambiare la destinazione a .NET Framework 4.

L'unica eccezione è Incorpora tipi di interoperabilità che presenta una dipendenza dai tipi disponibili solo in .NET Framework 4. Di conseguenza, non è possibile utilizzare tale funzionalità quando il progetto è destinato a .NET Framework versioni dalla 2.0 alla 3.5. Inoltre, i tipi contrassegnati come Variant sono contrassegnati così solo in .NET Framework 4, quindi nell'esempio precedente sarebbe stato necessario chiamare .Cast(Of T) se il progetto fosse stato destinato a .NET Framework versioni dalla 2.0 alla 3.5. È possibile, tuttavia, creare tipi Variant personalizzati (mediante i modificatori In/Out) il progetto è destinato a .NET Framework versioni dalla 2.0 alla 3.5.

Per modificare il framework di destinazione corrente per un'applicazione, fare doppio clic sul Progetto, fare clic sulla scheda Compilazione e su Opzioni di compilazione avanzate, quindi selezionare un'opzione dalla casella combinata nella parte inferiore.

Quando si compila dalla riga di comando, non è disponibile alcuna opzione della riga di comando per attivare questa funzionalità. Il compilatore esamina invece quale assembly ha fornito la definizione di System.Object (in genere mscorlib) e qual è il framework di destinazione dell'assembly, quindi registra tale valore nell'assembly di output (si tratta dello stesso meccanismo utilizzato dal compilatore durante la compilazione di assembly Silverlight). Quando si utilizza l'IDE, tutto ciò si verifica in modo trasparente, pertanto in generale non è necessario preoccuparsene.

Prova pratica

Come si può osservare, in Visual Basic 2010 sono disponibili molte potenti funzionalità che consentono una maggiore produttività con la scrittura di un minor numero di righe di codice, riducendo notevolmente il lavoro del compilatore. In questo articolo sono state esaminate solo le funzionalità specifiche del linguaggio, ma numerosi sono i miglioramenti apportati all'IDE di Visual Basic 2010. Eccone un elenco parziale:

  • Passa a
  • Evidenziazione dei riferimenti
  • Generazione dall'utilizzo
  • IntelliSense migliorato (corrispondenza di sottostringhe, ricerca della convenzione camel, modalità di suggerimento, utile per gli stili di sviluppo che prevedono test preliminari).
  • Supporto multimonitor
  • Zoom

Il team di Visual Basic ritiene importante ogni commento o suggerimento mirato a migliorare ulteriormente Visual Basic. Pertanto, è possibile inviare commenti e domande a Microsoft Connect. Per ulteriori informazioni sul linguaggio e sulle funzionalità dell'IDE, visitare msdn.com/vbasic, contenente articoli, esempi e video procedurali. Chiaramente, il modo migliore per imparare è approfondire le proprie conoscenze del prodotto e utilizzarlo, quindi è arrivato il momento di installarlo e di provarlo.

Ulteriori informazioni su Visual Basic? Eccole. MSDN Magazine *sta riprendendo la pubblicazione mensile della colonna Esigenze primarie che si rivolge soprattutto allo sviluppatore di Visual Basic scritta dal team di Microsoft Visual Basic.        *

Jonathan Anejaè un program manager del team di Entity Framework in Microsoft. È stato program manager del compilatore di Visual Basic durante le versioni di Visual Basic 2008 e Visual Basic 2010. Lavora in Microsoft da quattro anni.

Un ringraziamento ai seguenti esperti tecnici per la revisione dell'articolo: Dustin Campbell, Jason Malinowski e Lucian Wischik