Visual Basic 2010

Novedades de Visual Basic 2010

Jonathan Aneja

Desde su concepción en 1991, el lenguaje Visual Basic siempre ha sido una excelente herramienta de productividad para crear aplicaciones. Casi 20 años después, sigue proporcionando acceso fácil a Microsoft .NET Framework, lo que permite que los desarrolladores escriban aplicaciones que abarcan escritorios, teléfonos, exploradores e incluso la nube.

Este mes, Microsoft lanzará Visual Studio 2010, que incorpora la versión 10 de Visual Basic (a veces denominada VB 2010 o VB10). Esta versión, la más eficaz hasta ahora, contiene varias herramientas que ahorran tiempo para ayudar a los desarrolladores a lograr más con menos líneas de código. A continuación, le presentamos todo lo que debe saber para ganar terreno en la ejecución de Visual Basic en Visual Studio 2010.

Coevolución

En el pasado, equipos separados desarrollaron Visual Basic y C#, lo que a veces daba como resultado que aparecieran características en un lenguaje antes que en el otro. Por ejemplo, C# tenía propiedades de implementación automática e inicializadores de colección que no existían en Visual Basic y Visual Basic tenía características, tales como enlace en tiempo de ejecución y parámetros opcionales que no existían en C#. Pero siempre que aparecía una característica en uno de los lenguajes, los clientes solicitaban que dicha característica se agregara en el otro.

Para satisfacer estas demandas, Microsoft combinó los equipos de Visual Basic y C#, adoptando la estrategia de la coevolución. La intención es hacer que los lenguajes avancen juntos. Cuando se implementa una funcionalidad importante en un lenguaje, también debe aparecer en el otro. Esto no significa que todas las características estarán en ambos lenguajes ni que funcionen exactamente de la misma manera; de hecho, cada lenguaje tiene su propia historia, espíritu y sensibilidad, rasgos que es importante mantener. La coevolución no significa que cualquier tarea que se pueda realizar en un lenguaje deba ser igual de simple en el otro.

En .NET Framework 4, tanto Visual Basic como C# han dado grandes pasos hacia este objetivo, en que cada lenguaje ha agregado varias capacidades con que el otro ya contaba. La coevolución no sólo se trata del pasado, sino que también es la estrategia para la futura innovación en los lenguajes. Con este espíritu, .NET Framework 4 presenta nuevas características eficaces, tales como Dynamic Language Runtime, Embed Interop Types y varianza genérica, en ambos lenguajes de forma simultánea, lo que permite que los desarrolladores de Visual Basic y C# aprovechen plenamente la ventaja de .NET Framework.

Nuevas características de Visual Basic 2010

Las nuevas características de Visual Basic 2010 están diseñadas para ayudarlo a lograr más con menos líneas de código. Nosotros (el equipo de diseño de Visual Basic) buscamos los lugares donde los desarrolladores con frecuencia tienen que escribir abundante y tedioso código reutilizable e investigamos formas de hacer que el compilador lo hiciera por ellos. Esa es la imagen global; ahora, profundicemos en algunas características una por una.

Continuación de línea implícita

Visual Basic es un lenguaje orientado hacia líneas que usa sintaxis clara y similar al inglés para mejorar la legibilidad. Esto, sin embargo, con frecuencia resulta en código que se extiende más allá del límite de 80 caracteres por línea, lo que obliga a los desarrolladores a desplazarse mucho. Se puede usar el carácter de subrayado para indicar al compilador que debe continuar procesando la siguiente línea como parte de la línea actual (es decir, tratar varias líneas físicas como una única línea lógica). Sin embargo, tener que escribir caracteres de subrayado constantemente siempre ha sido molesto y, de hecho, durante años la principal solicitud ha sido que el compilador “lo deduzca solo”.

Bueno, en Visual Basic 2010 el compilador puede hacerlo. Ahora, éste sabe qué símbolos (tokens), tales como comas, paréntesis y operadores, tienden a ocurrir justo antes del carácter de continuación de línea e inserta el carácter para que los desarrolladores ya no tengan que hacerlo. Por ejemplo, terminar una instrucción de Visual Basic con una coma nunca es legal; el compilador lo sabe, de manera que cuando ve una secuencia de símbolos que se ve como {coma, entrar}, infiere la presencia del carácter de continuación de línea, como muestra el ejemplo de la figura 1.

Figura 1 Inferencia de continuación de línea

<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

En Visual Basic 2008, el código de la figura 1 hubiera necesitado nueve caracteres de subrayado. En cada uno de estos casos, sin embargo, el compilador infirió cuándo era necesario el carácter de subrayado y permitió que se omitiera:

  • Después del atributo <Extension()>
  • Después del ( (paréntesis de apertura) en la declaración de método
  • Después de la , (coma) en el primer parámetro
  • Antes del ) (paréntesis de cierre) en la declaración de método
  • Después del = (signo igual)
  • Después de <%= (etiqueta de apertura para una expresión incrustada)
  • Después de cada & (Y comercial) en el literal de XML
  • Antes de %> (etiqueta de cierre para una expresión incrustada)

Esta nueva capacidad del compilador es especialmente útil para la firma del método, que se extendería mucho más allá de los 80 caracteres en el ejemplo mostrado si cada parte estuviera en la misma línea. En la figura 2 verá todas las combinaciones de símbolos y las selecciones de ubicación en las que está implícito el carácter de continuación de línea.

Figura 2 Lugares donde los caracteres de continuación están implícitos

Símbolo Antes Después
, (coma),   .  (punto),   >  (atributos),   (  {  (corchetes de apertura),   <%=  (comenzar expresión incrustada (literales de XML))   X
),   },   ,   ]  (corchetes de cierre), %> (cerrar expresión incrustada) X  

Todas las palabras clave de 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

Operadores:

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

  X
Con (en un inicializador de objeto)   X

Como puede ver, existen más de 60 lugares en los que el lenguaje no requiere caracteres de subrayado. (De hecho, ninguno de los ejemplos de código de este artículo necesitaron el carácter de continuación de línea). Desde luego, se puede seguir usando el carácter de subrayado, de manera que el código de versiones anteriores de Visual Basic se seguirá compilando según lo esperado.

Lambdas de instrucción

El término “lambda” puede sonar intimidante al principio, pero una lambda es simplemente una función definida dentro de otra función. Visual Basic 2008 incluyó expresiones lambda con la palabra clave Function:

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

Las expresiones lambda brindan una manera sutil y compacta de expresar lógica de manera local sin tener que dividirla en varios métodos. Por ejemplo, así es como se habría visto el código anterior en Visual Basic 2005 (que no admitía expresiones lambda):

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

    ...

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

Desafortunadamente, las expresiones lambda de Visual Basic 2008 exigían que las expresiones devolvieran un valor, de modo que esto:

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

hubiera causado esto:

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

Console.WriteLine es un procedimiento Sub (void, en C#), por lo que no devuelve ningún valor y es por eso que el compilador indica un error. Para enfrentar esto, Visual Basic 2010 incluye compatibilidad con lambdas de instrucción, que son lambdas que pueden contener una o más instrucciones:

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

Dado que Console.WriteLine no devuelve ningún valor, simplemente podemos crear una lambda Sub en lugar de una lambda Function. El siguiente es otro ejemplo que usa varias instrucciones:

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

Cuando este código se ejecuta, imprime dos líneas por cada cliente. Tenga en cuenta también que si mantiene el mouse sobre c al codificar, verá que el compilador ha inferido el tipo como Customer (también es legal escribir c As Customer para establecer el tipo explícitamente). La transmisión dinámica de controladores de eventos es otro excelente uso para las lambdas de instrucción:

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

De hecho, es posible combinar lambdas de instrucción con una característica incluida en Visual Basic 2008: delegados relajados. (Puede usar delegados, es decir, punteros con seguridad de tipo a las funciones, para ejecutar varias funciones a la vez). Esta combinación produce una firma aun más simple:

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

Los delegados relajados permiten omitir completamente los parámetros de un controlador de eventos, un beneficio conveniente, dado que con frecuencia estos no se usan en absoluto, es decir, sólo agregan ruido visual.

Además de las lambdas Sub de una sola línea y las lambdas Sub de varias líneas que hemos visto hasta ahora, Visual Basic 2010 también admite lambdas Function de varias líneas:

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)

Otro aspecto interesante de las lambdas de instrucción es la forma en que forman intersección con los delegados anónimos que implementó Visual Basic 2008. Las personas con frecuencia los confunden con los métodos anónimos de C#, aunque técnicamente no son lo mismo. Los delegados anónimos ocurren cuando el compilador de Visual Basic infiere un tipo de delegado basado en la firma de método de una 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"))

Si ejecuta este código, observará que aparece el valor 4.5 en el cuadro de mensaje. Asimismo, si mantiene el mouse sobre method, verá el texto Dim method As <Function(String) As Double>. Dado que no proporcionamos ningún tipo de delegado real, el compilador generará uno automáticamente, de la siguiente forma:

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

A esto se le denomina delegado anónimo, dado que aparece sólo en el código producido por el compilador, no en el código escrito. Tenga en cuenta que el compilador infirió el tipo de retorno As Double, en circunstancias que en realidad no había ninguna cláusula As para especificar el tipo de retorno de la lambda. El compilador observa todas las instrucciones de retorno dentro de la lambda y ve los tipos 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

Después ejecuta el algoritmo de tipo dominante y determina que puede convertir con seguridad 10 en Double, pero no puede convertir con seguridad 4.5 en Integer; por lo que Double es su mejor opción.

También se puede tomar el control del tipo de retorno explícitamente, en cuyo caso, el compilador no intentará inferir el tipo. En lugar de confiar en el compilador para inferir el tipo de delegado, es muy común asignar una lambda a una variable que tiene un tipo de delegado explícito:

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

Dado que se proporcionó un tipo compatible explícito, no es necesario indicar As String o As Single;, ya que el compilador puede inferir su presencia según el tipo de delegado del lado izquierdo de la instrucción. De esta forma, si se mantiene el mouse sobre product, verá que el tipo inferido es String. Ya no es necesario especificar As Single, dado que el tipo de delegado ya proporcionó esa información. El ejemplo anterior, la firma del delegado Func (que .NET Framework incluye) se ve de la siguiente manera:

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

con una excepción menor, como veremos posteriormente en la sección Varianza genérica.

Propiedades de implementación automática

En Visual Basic, las propiedades son miembros de clase que se usan para exponer el estado de un objeto al mundo exterior. Una declaración de propiedad típica se ve de una forma similar a la siguiente:

Private _Country As String

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

Esas son 10 líneas de código para lo que en realidad es un concepto muy simple. Dado que los objetos típicos con frecuencia tienen miles de propiedades, se termina incluyendo mucho código reutilizable en las definiciones de clase. Para facilitar estas tareas, Visual Basic 2010 presenta propiedades de implementación automática, que permiten definir una propiedad simple mediante una sola línea de código:

Property Country As String

En este caso, el compilador se adelantará y generará Getter, Setter y los campos de copia de seguridad automáticamente. El nombre del campo de copia de seguridad siempre será un carácter de subrayado seguido por el nombre de la propiedad: _Country en este caso. Esta convención de nomenclatura asegura una compatibilidad de serialización binaria en caso de que una propiedad de implementación automática se cambie a una regular. Mientras el nombre del campo de respaldo siga siendo el mismo, la serialización binaria continuará funcionando.

Una de las cosas extraordinarias que puede hacer con propiedades de implementación automática es especificar inicializadores que establezcan el valor predeterminado de la propiedad cuando se ejecuta el constructor. Un escenario común en las clases de entidad, por ejemplo, establece la clave principal en algo similar a -1 para indicar que se encuentra en estado no guardado. Así es como se vería dicho código:

Property ID As Integer = -1

Cuando se ejecute el constructor, el campo de respaldo (_ID) se establecerá automáticamente en el valor -1. La sintaxis del inicializador también funciona para tipos de referencia:

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

La línea anterior de código puede no parecer muy propia de Visual Basic, dado que escribir el nombre del tipo dos veces es redundante. La buena noticia es que existe una sintaxis aun más corta que es coherente con lo que permite Visual Basic en declaraciones de variables regulares:

Property OrderList As New List(Of Order)

Esto incluso se puede combinar con inicializadores de objetos para permitir la configuración de propiedades adicionales:

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

Obviamente, para propiedades más complejas, sigue siendo necesaria la sintaxis extendida. Aún se puede escribir Property{Tab} para activar el antiguo fragmento de código de propiedad. Asimismo, después de escribir la primera línea de la propiedad, se puede escribir simplemente Get{Enter} y el IDE generará la propiedad en estilo antiguo:

Property Name As String
  Get
  End Get
  Set(ByVal value As String)
  End Set
End Property

Las personas comentan que la nueva sintaxis de propiedades es casi idéntica a la sintaxis de un campo público, entonces, ¿por qué no usar un campo público en su lugar? Hay algunas razones para esto:

  • Gran parte de la infraestructura de enlace de datos de .NET funciona en propiedades, pero no en campos.
  • Una interfaz no puede forzar la existencia de un campo; puede forzar la existencia de una propiedad.
  • Las propiedades otorgan más flexibilidad a largo plazo para reglas de negocios cambiantes. Por ejemplo, suponga que alguien incluye la regla de que un número de teléfono debe tener 10 dígitos. No hay forma de realizar una validación de esto al asignar a un campo público. El cambio de un campo público a una propiedad es un cambio innovador para escenarios como la serialización binaria y la reflexión.

Inicializadores de colección

Una práctica común de .NET es crear una instancia de colección y, después, llenarla llamando al método Add una vez para cada 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)

Pero el resultado es una gran cantidad de sobrecarga sintáctica para lo que es fundamentalmente un concepto muy simple. Visual Basic 2010 incluye los inicializadores de colección para permitirle crear instancias de colecciones más fácilmente. Con este código:

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

el comprador generará todas las llamadas al método Add automáticamente. También puede usar la característica con la sintaxis As New de Visual Basic:

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

Tenga en cuenta que en el equipo de Visual Basic siempre se recomienda usar la segunda sintaxis (As New) por sobre la primera, dado que hace al código resistente contra cambios en la configuración Option Infer.

Puede usar inicializadores de colección en cualquier tipo que cumpla con los siguientes requisitos:

  • Pueda iterar sobre el tipo mediante la instrucción For Each, es decir, implementa IEnumerable (para obtener una definición más precisa o detallada de un tipo de colección, consulte la sección 10.9.3 de la Especificación del lenguaje Visual Basic en msdn.microsoft.com/library/aa711986(VS.71).aspx).
  • Tenga un constructor accesible (no necesariamente público) sin parámetros.
  • Tenga una instancia o un método de extensión accesible (no necesariamente públicos) denominado Add.

Esto significa que también puede usar inicializadores de colección con tipos más complejos, como diccionarios:

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

(Observe que aunque esta instrucción abarca cinco líneas, no tiene caracteres de subrayado). En este caso, el compilador generará código equivalente a la manera antigua de inicializar el diccionario:

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

El compilador está llamando a un método Add que tiene dos parámetros en lugar de uno. Sabe cómo hacer esto debido a que los valores que pasaron al inicializador de colección eran llaves anidadas, como éstas: {{1, “One”}, {2, “Two”}, …}. Para cada conjunto de llaves anidadas, el compilador intenta pasar dichos parámetros a un método Add compatible.

También puede proporcionar su propia implementación personalizada de Add mediante un método Extension:

<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

(Observe todos los caracteres de subrayado que faltan). Este método extiende cualquier tipo que implemente IList(Of Customer) y después permite usar la nueva sintaxis de inicializador de colección de esta forma:

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

(agregar tres clientes a list). También puede usar los inicializadores de colección junto con propiedades de implementación automática:

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

Literales de matriz

Además de maneras más eficaces de trabajar con tipos de colecciones, Visual Basic 2010 también ofrece algunas excelentes mejoras para trabajar con matrices. Considere el siguiente código (que funciona bien en versiones antiguas):

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

Es obvio al observar los elementos de matriz que cada uno es un Integer, de moto que tener que escribir Integer dos veces en esta línea en realidad no aporta ningún valor. Los literales de matriz permiten la creación de una matriz al encerrar todos los elementos dentro de llaves y al hacer que el compilador infiera el tipo automáticamente después:

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

El tipo de numbers no es Object, sino Integer() (siempre que esté activado Option Infer), dado que el literal de matriz ahora puede ser autónomo y tener su propio tipo. Considere un ejemplo más complicado:

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

En este caso, el tipo de numbers se inferirá como Double(). El compilador determina el tipo examinando cada elemento de la matriz y calculando el tipo dominante (mediante el mismo algoritmo abordado anteriormente para inferir el tipo de retorno de una lambda de instrucción). ¿Y qué pasa si no hay un tipo dominante, como en el siguiente código?:

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

En este caso, convertir Integer en String sería una conversión de restricción (es decir, existiría la posibilidad de que se perdieran datos en runtime) y, de la misma forma, la conversión de String en Integer también sería una conversión de restricción. El único tipo seguro que se puede elegir es Object() (y el compilador devolverá un error cuando Option Strict esté activado).

Los literales de matriz se pueden anidar a partir de matrices multidimensionales o matrices escalonadas:

'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

Aunque, desde el punto de vista técnico, Visual Basic es un lenguaje fundamentalmente estático, siempre ha tenido capacidades dinámicas muy eficaces, como el enlace en tiempo de ejecución. Visual Studio 2010 incluye una nueva plataforma llamada Dynamic Language Runtime (DLR), que facilita la compilación de lenguajes dinámicos y la comunicación entre ellos. Visual Basic 2010 se ha actualizado completamente para admitir DLR en su enlace en tiempo de ejecución, para permitir que los desarrolladores usen bibliotecas y frameworks desarrollados en otros lenguajes, como IronPython o IronRuby.

Lo genial acerca de esta característica es que nada ha cambiado sintácticamente (de hecho, ni una sola línea de código se modificó en el compilador para admitir esta característica). Los desarrolladores pueden seguir haciendo operaciones con enlace en tiempo de ejecución de la misma forma que lo hacían en versiones anteriores de Visual Basic. Lo que ha cambiado es el código en el tiempo de ejecución de Visual Basic (Microsoft.VisualBasic.dll), que ahora reconoce la interfaz IDynamicMetaObjectProvider que ofrece DLR. Si un objeto implementa esta interfaz, el tiempo de ejecución de Visual Basic construirá DLR CallSite y permitirá que el objeto y el lenguaje que lo proporciona inyecten su propia semántica en la operación.

Por ejemplo, las bibliotecas de Python estándar contienen un archivo llamado random.py con un método llamado shuffle, que se puede usar para reorganizar de manera aleatoria los elementos de la matriz. Llamarlo es simple:

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)

En runtime, Visual Basic ve que el objeto implementa IDynamicMetaObjectProvider y, de esta forma, le entrega el control a DLR, que en ese momento se comunica con Python y ejecuta el método (pasando por la matriz Visual Basic como un argumento del método).

Ese es un ejemplo de cómo invocar API habilitada por DLR, pero los desarrolladores también pueden crear sus propias API que usen esta característica. La clave es implementar la interfaz IDynamicMetaObjectProvider, en cuyo caso, los compiladores Visual Basic y C# reconocerán que el objeto tiene semántica dinámica especial. En lugar de implementar la interfaz manualmente, es más fácil heredarla de la clase System.Dynamic.DynamicObject (que ya implementa esta interfaz) y sólo anular un par de métodos. La figura 3 muestra un ejemplo completo de la creación de un objeto dinámico personalizado (un “contenedor de propiedades” que aparece para crear propiedades sobre la marcha) y de cómo llamarlo mediante el enlace en tiempo de ejecución de Visual Basic. (Para obtener más información sobre cómo trabajar con DynamicObject, consulte el excelente artículo de Doug Rothaus disponible en blogs.msdn.com/vbteam/archive/2010/01/20/fun-with-dynamic-objects-doug-rothaus.aspx).

Figura 3 Crear un objeto dinámico personalizado y llamarlo con el enlace en tiempo de ejecución de 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 genérica

Esta es una característica que al principio puede parecer muy complicada (con términos como covarianza y contravarianza), pero en realidad es bastante simple. Si tiene un objeto de tipo IEnumerable(Of Apple) y desea asignarlo a un objeto IEnumerable(Of Fruit), esto debe ser legal, dado que cada Apple (manzana) es Fruit (fruta) (está obligado por una relación de herencia). Desafortunadamente, antes de Visual Basic 2010, el compilador no admitía varianza genérica, aunque en realidad sí se admitía en Common Language Runtime (CLR).

Consideremos el ejemplo de la figura 4. En Visual Basic 2008, el código de la figura 4 generaría un error de compilación (o, si Option Strict está desactivado, una excepción de runtime) en la línea Dim enabledOnly. La solución alternativa era llamar al método de extensión .Cast, como se muestra a continuación:

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

Esto ya no es necesario, dado que en Visual Basic 2010, la interfaz IEnumerable se ha marcado como covariante mediante el uso del modificador Out:

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

Figura 4 Un ejemplo de varianza genérica

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

Esto significa que el parámetro genérico T ahora es variante (es decir, funciona para relaciones de herencia) y el compilador se asegurará de que sólo se use en posiciones donde el tipo provenga de la interfaz. Los parámetros genéricos también pueden ser contravariantes, lo que significa que se usan en posiciones input. Un mismo tipo puede incluso tener ambos. Por ejemplo, el delegado Func que se analizó anteriormente tiene parámetros contravariantes (cosas que se pasan) y covariantes (para el tipo de retorno):

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

Puede usar los modificadores In y Out en sus interfaces y delegados personalizados. Muchas interfaces y delegados que se usan comúnmente en .NET Framework 4 ya se han marcado como variantes; ejemplos comunes incluyen todos los delegados Action/Func, IEnumerable(Of T), IComparer(Of T) e IQueryable(Of T), entre otros.

Lo que es interesante acerca de la varianza genérica es que es una característica por la que no es necesario preocuparse: si está haciendo su trabajo, usted nunca lo notará. Las situaciones que provocaban errores de compilación o que exigían una llamada a .Cast(Of T) deben seguir funcionando bien en Visual Basic 2010.

Parámetros opcionales mejorados

Los parámetros opcionales ofrecen una característica útil de productividad, que permite que los desarrolladores realicen métodos más flexibles y eviten llenar una clase con varias sobrecargas de un método. Una limitación del pasado era que los parámetros opcionales no podían aceptar valores NULL (o, de hecho, ningún tipo de estructura no intrinsic). Visual Basic 2010 ahora permite definir parámetros opcionales de cualquier tipo de valor:

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

En este caso, units es del tipo que acepta valores NULL(Of Integer) y backgroundColor es un tipo de estructura no intrinsic, pero aun así se pueden usar como parámetros opcionales. Visual Basic 2010 también ofrece mejor compatibilidad para parámetros opcionales que son genéricos.

Embed Interop Types

Para aplicaciones que realizan interoperabilidad COM, una dificultad común es tener que trabajar con ensamblados de interoperabilidad primarios (PIA). Un PIA es un ensamblado de .NET que sirve como contenedor RCW (Runtime Callable Wrapper) sobre un componente COM y tiene un GUID único para identificarlo. .Los ensamblados .NET se comunican con un PIA, que después realiza cualquier cálculo de referencias necesario para traspasar datos entre COM y .NET.

Desafortunadamente, los PIA pueden complicar la implementación, debido a que son DLL adicionales que se deben implementar en los equipos de los usuarios finales. También ocasionan problemas de control de versiones, por ejemplo, si se desea que una aplicación pueda funcionar en Excel 2003 y Excel 2007, se deben implementar ambos PIA con la aplicación.

La característica Embed Interop Types incrusta directamente en la aplicación sólo los tipos y los miembros del PIA que son absolutamente necesarios, lo que elimina la necesidad de implementar los PIA en los equipos de los usuarios finales.

Para activar esta función para un proyecto existente (ya está activada de manera predeterminada para futuras referencias), seleccione la referencia en Solution Explorer y cambie la opción Embed Interop Types en la ventana Properties (consulte la figura 5). Opcionalmente, si compila mediante el compilador de línea de comandos, use el conmutador /l (o /link) en lugar de /r y /reference.

image: Activación de la característica Embed Interop Types en Solution Explorer

Figura 5 Activación de la característica Embed Interop Types en Solution Explorer

Una vez que haya activado esta característica, la aplicación ya no depende del PIA. De hecho, si abre el ensamblado en Reflector o ildasm, observará que en realidad no existe ninguna referencia al PIA.

Compatibilidad con múltiples versiones (multi-targeting)

Lo más interesante acerca de todas las características de Visual Basic 2010 es que incluso puede usarlas en proyectos compatibles con .NET Framework 2.0 hasta .NET Framework 3.5. Esto significa que las características como continuación de línea implícita, literales de matriz, inicializadores recolección, lambdas de instrucción o propiedades de implementación automática siempre funcionarán en proyectos existentes sin tener que hacerlas compatibles con .NET Framework 4.

La única excepción es Embed Interop Types, que depende de tipos que sólo se encuentran en .NET Framework 4; como resultado, no puede usarla cuando es compatibles con las versiones 2.0 hasta 3.5 de .NET Framework. Asimismo, los tipos que están marcados como variantes sólo están marcados de esa forma en .NET Framework 4, de modo que, en el ejemplo anterior, de todas formas hubiera tenido que llamar a .Cast(Of T) cuando sea compatible con las versiones 2.0 a la 3.5. Sin embargo, puede hacer sus propios tipos de variantes (mediante los modificadores In/Out) cuando sea compatible con versiones anteriores.

Para cambiar el framework compatible actual de una aplicación, haga doble clic en My Project, haga clic en la pestaña Compile, haga clic en Advanced Compile Options y después seleccione del cuadro combinado de la parte inferior.

Al compilar desde la línea de comandos, de hecho no existe ningún conmutador de línea de comandos para activar esta característica. En su lugar, el compilador observa el ensamblado que se proporcionó en la definición de System.Object (generalmente mscorlib) y el framework con que es compatible dicho ensamblado, y después estampa el valor en el ensamblado de salida. (Este es el mismo mecanismo que usa el compilador cuando compila ensamblados Silverlight). Al usar el IDE, todo esto ocurre en forma transparente, por lo que, por lo general, no es algo de lo que haya que preocuparse.

Pruébelo

Como puede ver, Visual Basic 2010 posee muchas características eficaces que permiten ser más productivo, al escribir menos líneas de código y al descargar más trabajo en el compilador. En este artículo, sólo he abordado características de lenguaje, pero el IDE de Visual Basic 2010 tiene muchas otras mejoras. La siguiente es una lista parcial:

  • Navegar hasta
  • Resaltar referencias
  • Generar a partir del uso
  • Mejor IntelliSense (coincidencia de subcadenas, búsqueda de uso de mayúsculas y minúsculas como en Camel, modo de sugerencia, útiles para estilos de desarrollo de pruebas previas)
  • Compatibilidad con varios monitores
  • Zooming

El equipo de Visual Basic desea recibir sus comentarios acerca de lo que podemos hacer para mejorar aun más Visual Basic, por lo que lo invitamos a que nos envíe sus comentarios y preguntas a Microsoft Connect. Para obtener más información acerca del lenguaje y las características del IDE, consulte el contenido disponible en msdn.com/vbasic, incluidos los artículos, los ejemplos y los vídeos de procedimientos. Por supuesto, la mejor forma de aprender es interiorizándose en el producto y usándolo, de modo que es momento de instalarlo y probarlo.

¿Desea más Visual Basic? Ya lo tiene. MSDN Magazine *reanuda la publicación mensual de la columna Instintos básicos, que se centra en el desarrollador de Visual Basic y en temas relacionados y que redacta el equipo de Visual Basic de Microsoft.        *

Jonathan Anejaes un administrador de programas del equipo de Entity Framework de Microsoft. Anteriormente fue el administrador de programas del compilador de Visual Basic durante las versiones Visual Basic 2008 y Visual Basic 2010. Ha trabajado con Microsoft durante años.

Gracias a los siguientes expertos técnicos por su ayuda en la revisión de este artículo: Dustin Campbell, Jason Malinowski y Lucian Wischik