Exportar (0) Imprimir
Expandir todo
Expandir Minimizar
Personas que lo han encontrado útil: 1 de 2 - Valorar este tema

Información general sobre Visual Basic 9.0

Visual Studio 2005

Febrero de 2007

Publicado: 4 de Abril de 2007

Erik Meijer, Amanda Silver y Paul Vick
Microsoft Corporation

Resumen: Ofrece una introducción a las nuevas características y extensiones de lenguaje de Visual Basic que admiten programación con grandes cantidades de datos.

En esta página

Introducción Introducción
Información general sobre Visual Basic 9.0 Información general sobre Visual Basic 9.0
Variables locales con establecimiento implícito de tipos Variables locales con establecimiento implícito de tipos
Inicializadores de objetos y matrices Inicializadores de objetos y matrices
Tipos anónimos Tipos anónimos
Compatibilidad con XML Compatibilidad con XML
Comprensión de consultas Comprensión de consultas
Métodos de extensión y expresiones lambda Métodos de extensión y expresiones lambda
Tipos que aceptan valores Null Tipos que aceptan valores Null
Delegados relajados Delegados relajados
Conclusión Conclusión

Introducción

Visual Basic siempre se ha centrado en la creación de aplicaciones pragmáticas, orientadas a datos y empresariales. Mientras la migración a .NET llevó la eficacia de un marco unificado y una plataforma administrada al desarrollador de aplicaciones, la versión siguiente de Visual Basic incluye un conjunto de características que tienen como resultado un efecto profundo en la productividad del desarrollador al crear aplicaciones orientadas a datos. Estas extensiones de lenguaje introducen facilidades de consulta con finalidad general que se aplican a todas las fuentes de datos, ya sean gráficos de objeto jerárquicos y relacionales o documentos XML.

Este documento es una introducción informal a estas nuevas características. Para obtener más información, inclusive actualizaciones a la definición de lenguaje y vistas previas de compilador de Visual Basic, consulte el Centro para desarrolladores de Visual Basic (http://msdn.microsoft.com/vbasic/default.aspx).

Información general sobre Visual Basic 9.0

Para ver en la práctica la eficacia de estas características de lenguaje, empezaremos con un ejemplo del mundo real: la base de datos World Factbook de la CIA. La base de datos contiene variada información política, social, económica y geográfica acerca de los países de mundo. En nuestro ejemplo, empezaremos con un esquema para el nombre de cada país y su capital, superficie total y población. Representaremos este esquema en Visual Basic 9.0 mediante la clase siguiente (se usa pseudocódigo para una mayor brevedad):

Class Country
  Public Property Name As String
  Public Property Area As Long 
  Public Property Population As Integer
End Class

Éste es un pequeño subconjunto de la base de datos de países que usaremos como ejemplo:

Dim countries = { 
  New Country With { .Name = "Palau", .Area = 458, .Population = 16952 }, _
  New Country With { .Name = "Monaco", .Area = 1.9, .Population = 31719 }, _
  New Country With { .Name = "Belize", .Area = 22960, .Population = 219296 }, _
  New Country With { .Name = "Madagascar", .Area = 587040, .Population =
 13670507}}

Con esta lista, podemos consultar todos los países cuya población es menor de un millón de habitantes por medio de la siguiente expresión de consulta:

Dim smallCountries = From country In countries _
                     Where country.Population < 1000000 _
Select country

For Each country In SmallCountries
  Console.WriteLine(country.Name)
Next

Ya que sólo Madagascar tiene más de un millón de habitantes, el programa imprimiría la siguiente lista de nombres de países después de compilado y ejecutado:

Palau 
Monaco 
Belize

Examinemos el programa para entender las características de Visual Basic 9.0 que hicieron tan fácil escribirlo. En primer lugar, la declaración de cada una de las expresiones que representa los Countries usa la nueva sintaxis de inicializador de objeto New Country With {..., .Area = 458, ...} para crear una instancia de objeto compleja a través de una sintaxis concisa y basada en expresiones semejante a la declaración With.

La declaración ilustra también las declaraciones de variable local con establecimiento implícito de tipos , en las que el compilador infiere el tipo de la variable local Countries de la expresión del inicializador situada a la derecha de la declaración. La anterior declaración es equivalente a una declaración de variable local con establecimiento implícito de tipos del tipo Country().

Dim countries As Country() = {...}

Hacemos hincapié en que sigue tratándose de una declaración con establecimiento inflexible de tipos; el compilador ha inferido automáticamente el tipo del lado derecho de la declaración local y no hay necesidad de que el programador introduzca ese tipo de forma manual en el programa.

La declaración de variable local SmallCountries se inicializa con una expresión de consulta de estilo SQL para filtrar todos los países con menos de un millón de habitantes. La semejanza con SQL es intencionada, lo que permite a los programadores que ya saben SQL iniciarse en la sintaxis de consultas de Visual Basic más rápidamente.

Dim smallCountries = From country In Countries _
                     Where country.Population < 1000000 _
   Select country

Tenga en cuenta que este código de ejemplo presenta otra aplicación con establecimiento implícito de tipos: el compilador infiere el tipo de SmallCountries como IEnumerable (Of Country) basado en el tipo de resultado de la expresión de consulta. El compilador traduce la expresión de consulta en llamadas a la API compatible con LINQ, que implementa los operadores de consulta para todos los tipos que implementan IEnumerable (Of T). En este caso, la traducción es tan sencilla como la siguiente:

Dim smallCountries As IEnumerable(Of Country) = _
   Countries.Where(Function(country) country.Population < 1000000). _
             Select(Function(country) country)

La sintaxis ampliada depende de las expresiones lambda , que representan las funciones en línea que devuelven el resultado de una expresión. La expresión lambda se convierte en un delegado y se pasa a la función de extensión Where, que se define en la biblioteca estándar de operadores de consulta como una extensión de la interfaz IEnumerable (Of T).

Ahora que hemos visto algunas de las nuevas características de Visual Basic 9.0, tratemos el tema con mayor detalle.

Variables locales con establecimiento implícito de tipos

En una declaración de variable local con establecimiento implícito de tipos, el tipo de variable local se infiere de la expresión de inicializador en el lado derecho de una instrucción de declaración local. Por ejemplo, el compilador infiere los tipos de las siguientes declaraciones variables:

Dim population = 31719
Dim name = "Belize"
Dim area = 1.9
Dim country = New Country With { .Name = "Palau", ...}

De este modo, son exactamente equivalentes a las siguientes declaraciones con tipo explícito:

Dim population As Integer = 31719
Dim name As String = "Belize"
Dim area As Float = 1.9
Dim country As Country = New Country With { .Name = "Palau", ...}

Debido a que los tipos de declaraciones de variable local se infieren con la nueva opción Option Infer On (predeterminada en los nuevos proyectos), independientemente de la configuración de Option Strict, el acceso a dichas variables siempre se enlaza en tiempo de compilación. El programador debe especificar explícitamente el enlace en tiempo de ejecución en Visual Basic 9.0, declarando explícitamente las variables del tipo Object, como se aprecia a continuación:

Dim country As Object = New Country With { .Name = "Palau", ... }

El hecho de inferir tipos previene el uso accidental de enlaces en tiempo de ejecución y, lo que es más importante, permite que las extensiones eficaces enlacen a tipos de datos nuevos tales como XML, como veremos más adelante.

La variable de control de bucle en una declaración For...Next o For Each...Next puede ser también una variable con establecimiento implícito de tipos. Cuando se especifica la variable de control de bucle, como en For I = 0 To SmallCountries.Count o For Each country In smallCountries, el identificador define una nueva variable local con establecimiento implícito de tipos, cuyo tipo se infiere de la expresión de inicializador o colección y se limita al bucle completo. Con esta aplicación de inferencia de tipos, podemos volver a escribir el bucle que imprime todos los países pequeños de la siguiente manera:

For Each country In smallCountries
  Console.WriteLine(country.Name)
Next

El tipo de country se infiere para ser Country, el tipo de elemento de SmallCountries.

Inicializadores de objetos y matrices

En Visual Basic, la declaración With simplifica el acceso a varios miembros de un valor agregado sin especificar las expresión de destino varias veces. Dentro del bloque de instrucciones With, se evalúa una expresión de acceso de miembro que empieza con un punto como si éste fuera precedido por la expresión de destino de la declaración With. Por ejemplo, las declaraciones siguientes inicializan una instancia nueva Country y, posteriormente, inicializan sus campos a los valores necesarios:

Dim palau As New Country()
With palau
  .Name = "Palau"  
  .Area = 458
  .Population = 16952
End With

Los nuevos inicializadores de objeto de Visual Basic 9.0 son una forma basada en expresiones de With para crear instancias de objeto complejas de forma concisa. Con los inicializadores de objeto, podemos capturar las dos declaraciones anteriores en una sola declaración local (con establecimiento de tipos implícito), como se aprecia a continuación:

Dim palau = New Country With { _
  .Name = "Palau", _
  .Area = 458, _
  .Population = 16952 _
}

Este estilo de inicialización de objetos con expresiones es importante para las consultas. Normalmente, una consulta se parece a una declaración de objeto inicializada por una cláusula Select a la derecha del signo de igual. Ya que la cláusula Select devuelve una expresión, debemos poder inicializar el objeto completo con una sola expresión.

Como hemos comprobado, los inicializadores de objeto son también útiles para crear colecciones de objetos complejos. Se pueden inicializar las matrices e inferir los tipos de elemento usando una expresión de inicializador de matriz . Por ejemplo, dada la declaración para ciudades como clase,

Class City
  Public Property Name As String
  Public Property Country As String
  Public Property Longitude As Long 
  Public Property Latitude As Long
End Class

podemos crear una matriz de capitales para los países de nuestro ejemplo de la siguiente manera:

Dim Capitals = { _
  New City With { _
    .Name = "Antanarivo", _
    .Country = "Madagascar", _
    .Longitude = 47.4, _
    .Latitude = -18.6 }, _
  New City With { _
    .Name = "Belmopan", _
    .Country = "Belize", _
    .Longitude = -88.5, _
    .Latitude = 17.1 }, _
  New City With { _
    .Name = "Monaco", _
    .Country = "Monaco", _
    .Longitude = 7.2, _
    .Latitude = 43.7 }, _
  New City With { _
    .Country = "Palau",
    .Name = "Koror", _
    .Longitude = 135, _
    .Latitude = 8 } _
}

Tipos anónimos

A menudo, sólo deseamos quitar, o sacar de la proyección, determinados miembros de un tipo como resultado de una consulta. Por ejemplo, puede que deseemos saber sólo el Name y Country de todas las capitales tropicales, usando las columnas Latitude y Longitude en los datos de origen para identificar los trópicos, pero sacando de la proyección esas columnas en el resultado. En Visual Basic 9.0, lo hacemos creando una nueva instancia de objeto (sin dar nombre al tipo) para cada ciudad C cuya latitud esté entre el trópico de Cáncer y el trópico de Capricornio:

Const TropicOfCancer = 23.5
Const TropicOfCapricorn = -23.5

Dim tropical = From city In Capitals _
               Where TropicOfCancer <= city.Latitude _
                   AndAlso city.Latitude >= TropicOfCapricorn _
               Select New With {city.Name, city.Country}

El tipo inferido de la variable local Tropical es una colección de instancias de tipo anónimo, es decir (usando pseudocódigo), IEnumerable(Of { Name As String, Country As String }). El compilador de Visual Basic creará una clase implícita, por ejemplo, _Name_As_String_Country_As_String_, cuyos nombres y tipos de miembro se infieren del inicializador objetivo, como se aprecia a continuación:

Class _Name_As_String_Country_As_String_ 
    Public Property Name As String
    Public Property Country As String
    ...
End Class

Dentro del mismo programa, el compilador unirá los tipos anónimos idénticos. Dos inicializadores de objeto anónimos que especifican una secuencia de propiedades de los mismos nombres y tipos en el mismo orden producirán instancias del mismo tipo anónimo. Externamente, los tipos anónimos generados por Visual Basic se borran a Object, que permite al compilador pasar uniformemente los tipos anónimos como argumentos y resultados de funciones.

Ya que los tipos anónimos normalmente se usan para proyectar miembros de un tipo existente, Visual Basic 9.0 permiten la notación de proyección abreviada New With { city.Name, city.Country } para reducir la notación de formato largo New With { .Name = city.Name, .Country = city.Country }. Cuando se usa en la expresión de resultado de una comprensión de consulta, podemos abreviar aún más los inicializadores de proyección de la siguiente manera:

Dim Tropical = From city In Capitals _
               Where TropicOfCancer <= city.Latitude _
                   AndAlso city.Latitude >= TropicOfCapricorn _
               Select city.Name, city.Country

Tenga en cuenta que el significado de ambas formas abreviadas es idéntico al de la forma de formato largo a la que nos referimos anteriormente.

Compatibilidad con XML

LINQ para XML es una nueva API de programación XML en memoria diseñada específicamente para aprovechar las últimas capacidades de .NET Framework, como el marco de Consultas integradas en los lenguajes (Language Integrated Query, LINQ). Al igual que las comprensiones de consulta agregan una sintaxis familiar y útil a los operadores de consulta estándar subyacentes de .NET Framework, Visual Basic 9.0 ofrece gran compatibilidad con LINQ para XML a través de los literales XML y las propiedades XML .

Para ilustrar los literales XML, permítanos consultar los orígenes de datos relacionales planos Countries y Capitals para crear un modelo XML jerárquico que anide la capital de cada país como un elemento secundario y calcule la densidad de población como un atributo.

Para encontrar la capital de un país determinado, aplicamos una combinación al miembro name de cada país con el miembro country de cada ciudad. Dado un país y su capital, podemos construir fácilmente el fragmento XML llenando huecos de expresión incrustados con valores computados. Escribimos un "hueco" para una expresión de Visual Basic con sintaxis que recuerda a ASP, como en Name=<%= country.Name %> o <Name><%= city.Name %></Name>. Ésta es nuestra consulta, que combina literales XML y comprensiones de consulta:

Dim countriesWithCapital As XElement = _
    <Countries>
    <%= From country In Countries, city In Capitals _
        Where country.Name = city.Country _
        Select <Country Name=<%= country.Name %>
                        Density=<%= country.Population / country.Area %>>
                  <Capital>
                   <Name><%= city.Name %></Name>
                   <Longitude><%= city.Longitude %></Longitude>
                   <Latitude><%= city.Latitude %></Latitude>
                  </Capital>
               </Country> _
    %>
    </Countries>

Tenga en cuenta que el tipo XElement podría ser omitido de la declaración, en cuyo caso se inferirá como cualquier otra declaración local.

En esta declaración, el resultado de la consulta Select se debe sustituir dentro del elemento <Countries>. Por tanto, la consulta Select es el contenido del primer "hueco", enmarcado por las etiquetas de estilo ASP conocidas <%=y %> en <Countries>. Ya que el resultado de una consulta Select es una expresión y los literales XML son expresiones, es lógico anidar otro literal XML en el propio Select. Este literal anidado contiene "huecos" de atributo anidados para Country.Name y la proporción de densidad de población computada Country.Population/Country.Area, y "huecos"de elemento anidados para el nombre y las coordenadas de la capital.

Una vez compilada y ejecutada, la consulta anterior devolverá el documento XML siguiente (vuelto a formatear ligeramente a partir de la impresión del IDE estándar para ahorrar espacio):

<Countries>
 <Country Name="Palau" Density="0.037117903930131008">
   <Capital>
     <Name>Koror</Name><Longitude>135</Longitude><Latitude>8</Latitude>
   </Capital>
 </Country>
 <Country Name="Monaco" Density="16694.21052631579">
   <Capital>
     <Name>Monaco</Name><Longitude>7.2</Longitude><Latitude>3.7</Latitude>
   </Capital>
 </Country>
 <Country Name="Belize" Density="9.5512195121951216">
   <Capital>
     <Name>Belmopan</Name><Longitude>-88.5</Longitude><Latitude>17.1</Latitude>
   </Capital>
 </Country>
 <Country Name="Madagascar" Density="23.287181452711909">
   <Capital>
     <Name>Antananarivo</Name>
     <Longitude>47.4</Longitude><Latitude>-18.6</Latitude>
   </Capital>
  </Country>
</Countries>

Visual Basic 9.0 compila literales XML en objetos normales System.Xml.Linq, lo que garantiza una completa interoperabilidad entre Visual Basic y otros lenguajes que usan LINQ para XML. Para nuestra consulta de ejemplo, el código producido por el compilador (si lo pudiéramos consultar) sería el siguiente:

Dim countriesWithCapital As XElement = _ 
  New XElement("Countries", _
        From country In Countries, city In Capitals _
        Where country.Name = city.Country _
  Select New XElement("Country", _
             New XAttribute("Name", country.Name), _
             New XAttribute("Density", country.Population/country.Area), _
             New XElement("Capital", _
               New XElement("Name", city.Name), _
               New XElement("Longitude", city.Longitude), _
               New XElement("Latitude", city.Latitude))))

Además de crear XML, Visual Basic 9.0 simplifica también el acceso a las estructuras XML por medio de las propiedades XML; es decir, los identificadores del código de Visual Basic están ligados en tiempo de ejecución a los elementos y atributos XML. Por ejemplo, podemos imprimir la densidad de población de todos países del ejemplo de la siguiente manera:

  • Use el eje secundario countriesWithCapital.<Country> para obtener todos los elementos "Country " de la estructura XML countriesWithCapital.

  • Use el eje de atributo country.@Density para obtener el atributo "Density" del elemento Country.

  • Use el eje de descendientes country...<Latitude> (escrito literalmente, con tres puntos como en el código original) para obtener los atributos secundarios "Latitude" del elemento Country, independientemente de su posición en la jerarquía.

  • Use la propiedad de extensión .Value de IEnumerable (Of XElement) para seleccionar el valor del primer elemento de la secuencia resultante, o el indizador de la extensión (i) para seleccionar el elemento i-th.

Al reunir todas estas características, el código se puede condensar y simplificar considerablemente:

For Each country In countriesWithCapital.<Country>
  Console.WriteLine("Density = " & country.@Density)
  Console.WriteLine("Latitude = " & country...<Latitude>.Value)
Next

El compilador sabe usar el enlace en tiempo de ejecución sobre objetos normales cuando la expresión de destino de una declaración, asignación o inicialización es del tipo Object en lugar de un tipo más específico. Igualmente, el compilador sabe usar el enlace en tiempo de ejecución sobre XML cuando la expresión de destino es del tipo, o de la colección de, XElement, XDocument o XAttribute.

Como resultado del enlace en tiempo de ejecución sobre XML, el compilador se traduce de la siguiente manera:

  • La expresión de eje secundario countriesWithCapital.<Country> se traduce en la llamada sin procesar a LINQ para XML countriesWithCapital.Elements("Country"), que devuelve la colección de todos los elementos secundarios denominados "Country" del elemento Country.

  • La expresión de eje de atributo country.@Density se traduce en Country.Attribute("Density").Value, que devuelve el atributo secundario único denominado "Density" de Country;.

  • La expresión de eje descendente country...<Latitude> se traduce en la llamada sin procesar a LINQ para XML country.Descendants("Latitude"), que devuelve la colección de todos los elementos por debajo de country.

Comprensión de consultas

Un operador de consulta es un operador como Select, Order By o Where que se puede aplicar de una sola vez a una colección de valores de la colección completa. Una expresión de consulta es una expresión que aplica una serie de operadores de consulta a una colección concreta. Por ejemplo, la siguiente expresión de consulta toma una colección de países y devuelve los nombres de todos los países con menos de un millón de habitantes:

Dim smallCountries = From country In Countries _
                     Where country.Population < 1000000 _
                     Select country

La sintaxis de la expresión de consulta se diseña para ser razonablemente aproximada a la sintaxis SQL relacional estándar, con la intención de que cualquiera que esté familiarizado con SQL pueda usar las expresiones de consulta con muy pocas indicaciones. No obstante, la sintaxis no está restringida por SQL, ni existen expresiones de consulta ideadas para ser una traducción de SQL en Visual Basic. Debido a que SQL fue diseñado en torno a un modelo puramente relacional, algunas de sus expresiones no funcionan en un sistema de tipos que permite, e incluso abraza, la jerarquización. Asimismo, parte de los elementos sintácticos y semánticos de SQL entra en conflicto o no se integra bien con la sintaxis o semántica existente de Visual Basic. De este modo, mientras las expresiones de consulta deben ser familiares para cualquiera que conozca SQL, habrá diferencias que requerirán cierto aprendizaje.

Las expresiones de consulta se traducen en llamadas a los operadores de secuencia subyacentes de los tipos que admiten consultas especificados como tipo origen en la cláusula From. Dado que los operadores de secuencia se suelen definir como métodos de extensión en el tipo de origen, se enlazan a cualquier operador de secuencia que se encuentre en el ámbito. Esto implica que, al importar una implementación particular, la sintaxis de la expresión de consulta ser puede enlazar de nuevo a distintas API compatibles con LINQ. Así es como se pueden enlazar de nuevo las expresiones de consulta a una implementación que use LINQ para SQL o LINQ a objetos (un motor de ejecución de consultas local que ejecuta la consulta en memoria).

Algunos operadores de consulta, como From, Select y Group By, introducen una clase especial de variable local llamada variable de rango. De forma predeterminada, una variable de rango abarca desde el operador de introducción hasta un operador que esconde la variable de rango y representa una propiedad o columna de la fila individual de una colección durante la evaluación de la consulta. Por ejemplo, en la consulta siguiente:

Dim smallCountries = From country In Countries _
                     Where country.Population < 1000000 _
                     Select country

El operador From introduce una variable de rango country con tipo "Country". El siguiente operador de consulta Where hace referencia a la variable de rango country para representar a cada cliente individual en la expresión de filtro country.Population < 1000000.

Algunos operadores de consulta, como Distinct, no usan ni cambian la variable de control. Otros operadores de consulta, como Select, esconden las variables de rango actuales en el ámbito e introducen otras nuevas. Por ejemplo, en la consulta:

Dim smallCountries = From country In Countries _
                     Select country.Name, Pop = country.Population
                     Order By Pop

El operador de consulta Order By sólo tiene acceso a las variables de rango Name y Pop introducidas por el operador Select; si el operador Order By tratara de hacer referencia a Country, se produciría un error en el tiempo de compilación.

Si una consulta termina sin un operador Select, el tipo de elemento resultante de la colección será como si se presentara una proyección Select con todas las variables de control en el ámbito:

Dim countriesWithCapital = From country In Countries, city In Capitals _
                           Where country.Name = city.Country
The inferred type for this local declaration is (in pseudo-code to represent an 
anonymous type) IEnumerable(Of { Country As Country, City As City }).

Diferencias entre las expresiones de consulta y SQL: composicionalidad y datos jerárquicos

Las expresiones de consulta de Visual Basic 9.0 son completamente composicionales, lo que significa que esas comprensiones de consulta se pueden anidar o construir arbitrariamente anexando una consulta con operadores de consulta adicionales. La composicionalidad simplifica la comprensión de una consulta grande al entender cada subexpresión individual de forma aislada, al igual que simplifica el seguimiento de la semántica y tipos que fluyen en cada operador de consulta. Sin embargo, la composicionalidad como principio de diseño proporciona una experiencia bastante diferente al escribir una consulta a partir de SQL, donde las consultas se analizan como un bloque monolítico.

Asimismo, las API compatibles con LINQ tienden a implementar los operadores de secuencia con ejecución diferida. Ejecución diferida significa que la consulta no se evalúa hasta que se enumeran los resultados. En LINQ para SQL, significa que la consulta no se envía de forma remota a SQL hasta que se solicitan los resultados. Esto significa que el hecho de separar las consultas en varias declaraciones no se traduce en que la base de datos se consulte varias veces. Como resultado, lo que normalmente sería una consulta anidada en SQL, pasa a ser una consulta composicional en LINQ.

Uno de los motivos por los que SQL carece de composicionalidad es que el modelo de datos relacional subyacente no es composicional. Por ejemplo, puede que las tablas no contengan subtablas; es decir, todas tablas deben ser planas. Como resultado, en lugar de dividir las expresiones complejas en unidades más pequeñas, los programadores de SQL escriben expresiones monolíticas cuyos resultados son tablas planas, de modo que encajan con el modelo de datos de SQL. Ya que Visual Basic se basa en el sistema de tipos CLR, no existen restricciones en cuanto a qué tipos pueden aparecer como componentes de otros tipos. Aparte de las reglas de tipos estáticos, no existen restricciones en cuanto a la clase de expresiones que pueden aparecer como componentes de otras expresiones. Como resultado, no sólo los objetos, filas y XML, sino también Active Directory, los archivos, las entradas de registro, etc., son todos ciudadanos de primera clase de los orígenes y resultados de las consultas.

Operadores de consulta

Aquellos que estén familiarizados con la implementación de SQL reconocerán en los operadores subyacentes de la secuencia .NET muchos de los operadores composicionales del álgebra relacional, como la proyección, selección, productos cruzados, agrupación y clasificación, que representa los planes de consulta dentro del procesador de consultas.

  • El operador From introduce una o varias variables de rango y especifica una colección que consultar o computa un valor para la variable de rango.

  • El operador Select especifica la forma de la colección de salida.

  • Los operadores Where y Distinct restringen los valores de la colección.

  • El operador Order By impone una ordenación de la colección

  • Los operadores Skip, Skip While, Take y Take While devuelven un subconjunto de una colección basado en una orden o condición.

  • Los operadores Union, Union All, Except e Intersect toman dos colecciones y producen una sola colección.

  • El operador Group By agrupa la colección basada en una o más claves.

  • Los operadores Avg, Sum, Count, Min y Max agregan una colección y producen un valor.

  • Los operadores Any y All agregan una colección y devuelven un valor booleano basado en una condición.

  • El operador Join toma dos colecciones y produce una sola colección basada en las claves correspondientes derivadas de los elementos.

  • El operador Group Join realiza una combinación agrupada de dos colecciones basadas en claves correspondientes extraídas de los elementos.

La sintaxis completa para todos los operadores se puede encontrar en la especificación completa del lenguaje. Sin embargo, para propósitos ilustrativos, el siguiente código encuentra las capitales de cada país y ordena los nombres de los países según la latitud de la capital:

Dim countriesWithCapital = _
  From country In Countries _
  Join city In Capitals On country.Name Equals city.Country _
  Order By city.Latitude _
  Select country.Name

Para las consultas que computan un valor basado en una colección, el operador Aggregate se aplica a la colección. La siguiente consulta encuentra el número de países pequeños y computa su densidad media en una declaración:

Dim popInfo = _
  Aggregate country In Countries _
  Where country.Population < 1000000 _
  Into Total = Count(), Density = Average(country.Population/country.Area)

Las funciones de agregado suelen aparecer con mayor frecuencia en combinación con la partición de la colección de origen. Por ejemplo, podemos agrupar todos los países según si son tropicales o no, y, posteriormente, agregar el recuento de cada grupo. Para ello, los operadores de agregado se pueden usar junto con las cláusulas Group By y Group Join. En el siguiente ejemplo, la función auxiliar, IsTropical, encapsula la comprobación de si una City tiene clima tropical:

  Function IsTropical() As Boolean
    Return TropicOfCancer =< Me.Latitude AndAlso Me.Latitude >= 
TropicOfCapricorn
  End Function

A partir de esta función auxiliar, usamos exactamente la misma agregación anterior, pero primero realizamos particiones de la colección de entrada de los pares Country y Capitalen grupos para los que Country.IsTropical es el mismo. En este caso, hay dos grupos de este tipo: uno que contiene los países tropicales Palaos, Belice y Madagascar; y otro que contiene el país no tropical Mónaco.

Clave

País

Ciudad

Country.IsTropical() = True

Palaos
Belice
Madagascar

Koror
Belmopán
Antanarivo

Country.IsTropical() = False

Mónaco

Mónaco

A continuación, agregamos los valores a estos grupos, computando el recuento total y la densidad media. El tipo de resultado es ahora una colección de pares de Total As Integer y Density As Double:

Dim countriesByClimate = _
  From country In Countries _
 Join city In Capitals On country.Name Equals city.Country _
 Group By country.IsTropical()
 Into Total = Count(), Density = Average(country.Population/country.Area)

La anterior consulta esconde una complejidad considerable. La siguiente consulta produce los mismos resultados usando expresiones lambda y métodos de extensión para expresar la consulta con sintaxis de llamada de método.

Dim countriesByClimate7 = _
  countries. _
    SelectMany( _
      Function(country) Capitals, _ 
      Function(country, city) New With {country, city}). _
    Where(Function(it) it.country.Name = it.city.Country). _ 
    GroupBy( _
      Function(it) it.city.IsTropical(), _
      Function(IsTropical, Group) _
        New With { _
          IsTropical, _
          .Total = Group.Count(), _
          .Density = Group.Average( _
             Function(it) it.country.Population / it.country.Area _
          ) _
        } _
    )

Métodos de extensión y expresiones lambda

Gran parte de la eficacia subyacente de la infraestructura de consulta estándar de .NET Framework tiene su origen en los métodos de extensión y expresiones lambda . Los métodos de extensión son métodos compartidos marcados con atributos personalizados que les permiten ser invocados con la sintaxis de métodos de instancia. La mayoría de los métodos de extensión tienen firmas semejantes. El primer argumento es la instancia a la cual se aplica el método y el segundo es el predicado que se va a aplicar. Por ejemplo, el método Where, en el que se traduce la cláusula Where, tiene la siguiente firma:

Module IEnumerableExtensions
  <Extension> _
  Function Where (Of TSource) _
    (Source As IEnumerable(Of TSource), _
     predicate As Func(Of TSource, Boolean)) As IEnumerable(Of TSource)
    ...
  End Function
End Module

Dado que muchos de los operadores de consulta estándar como Where, Select, SelectMany, etc., se definen como métodos de extensión que toman los delegados del tipo Func(Of S,T) como argumentos, el compilador abstrae el requisito de producir los delegados que representan el predicado. El compilador crea cierres , delegados que capturan el contexto que los rodea, y los pasa a la llamada de método subyacente. Por ejemplo, con la siguiente consulta, el compilador genera las expresiones lambda que representan los delegados para ser pasadas a las funciones Select y Where:

Dim smallCountries = From country In countries _
                     Where country.Population < 1000000 _
                     Select country.Name

El compilador genera dos expresiones lambda para la proyección y el predicado respectivamente:

Function(Country As Country) country.Name
Function (Country As Country) country.Population < 1000000

Y la anterior consulta se traduce en el marco de las llamadas de método, pasando el origen y la expresión lambda para aplicar como argumentos:

Dim smallCountries = _
  Enumerable.Select( _
      Enumerable.Where(countries, _
        Function (country As Country) country.Population < 1000000), _
        Function(country As Country) country.Name)

Tipos que aceptan valores Null

Las bases de datos relacionales presentan semánticas para los valores que admiten Null, que son a menudo incoherentes con los lenguajes de programación habituales; además, con frecuencia los programadores no están familiarizados con ellas. En aplicaciones con grandes cantidades de datos, resulta de gran importancia que estos programas manejen esta semántica de forma clara y correcta. Reconociendo esta necesidad, con .NET Framework 2.0, CLR ha agregado compatibilidad con el tiempo de ejecución para la posibilidad de admitir valores Null mediante el tipo genérico Nullable (Of T As Structure). Al usar este tipo, podemos declarar las versiones que admiten Null de tipos de valores como Integer, Boolean, Date, etc. Por motivos que se harán patentes, la sintaxis de Visual Basic para tipos que admiten valores Null es T?.

Por ejemplo, debido a que no todos países son independientes, podemos agregar un miembro nuevo a la clase Country que representa su fecha de independencia, en caso de ser aplicable:

Partial Class Country
  Public Property Independence As Date?
End Class

La fecha de la independencia de Palaos es #10/1/1994#, pero las Islas Vírgenes Británicas son una zona dependiente del Reino Unido y de ahí que su fecha de independencia sea Nothing.

Dim palau = _
  New Country With { _
    .Name = "Palau", _
    .Area = 458, _
    .Population = 16952, _
    .Independence = #10/1/1994# }

Dim virginIslands = _
  New Country With { _
    .Name = "Virgin Islands", _
    .Area = 150, _
    .Population = 13195, _
    .Independence = Nothing }

Visual Basic 9.0 admitirá la lógica con tres valores y la aritmética de propagación de valores Null en valores que admiten Null; esto significa que, si uno de los operandos de una aritmética, comparación (lógica o bit a bit), variación, cadena u operación de tipos es Nothing, el resultado será Nothing. Si ambos operandos son valores apropiados, la operación se realiza en los valores subyacentes de los operandos y el resultado aceptará valores Null.

Dado que tanto Palau.Independence como VirginIslands.Independence tienen el tipo Date?, el compilador usará una aritmética de propagación de valores Null para las siguientes sustracciones y, por esto, el tipo inferido para la declaración local PLength y VILength será TimeSpan?.

Dim pLength = #8/24/2005# - Palau.Independence          ‘ 3980.00:00:00

El valor de PLength es 3980.00:00:00 porque ninguno de los operandos es Nothing. Por otro lado, ya que el valor de VirginIslands.Independence es Nothing, el resultado es de nuevo de tipo TimeSpan?, pero el valor de VILength será Nothing a causa de la propagación de valores Null.

Dim vILength = #8/24/2005# - virginIslands.Independence ‘ Nothing

Como en SQL, los operadores de comparación llevarán a cabo la propagación de valores Null y los operadores lógicos usarán la lógica de tres valores. En las declaraciones If y While, Nothing se interpreta como False; por esto, en el siguiente fragmento de código, se toma la rama Else:

If vILength < TimeSpan.FromDays(10000)
  ...
Else
  ...
End If

Hay que tener en cuenta que, bajo la lógica de tres valores, las comprobaciones de igualdad X = Nothing y Nothing = X siempre se evalúan como Nothing; para comprobar si X es Nothing, debemos usar la comparación de lógica de dos valores X Is Nothing o Nothing Is X.

Delegados relajados

Al crear un delegado por medio de AddressOf o Handles en Visual Basic 8.0, uno de los métodos abordados para enlazar el identificador del delegado debe coincidir exactamente con la firma del tipo del delegado. En el siguiente ejemplo, la firma de la subrutina OnClick debe coincidir exactamente con la firma del delegado de controlador de eventos Delegate Sub EventHandler(sender As Object, e As EventArgs), que se declara en segundo plano en el tipo Button:

Dim WithEvents btn As New Button()

Sub OnClick(sender As Object, e As EventArgs) Handles B.Click
  MessageBox.Show("Hello World from" & btn.Text)
End Sub

Sin embargo, al invocar las funciones y subrutinas no delegadas, Visual Basic no requiere los argumentos reales para coincidir exactamente con uno de los métodos que tratamos de invocar. Como se muestra en los siguientes fragmentos, podemos invocar la subrutina OnClick mediante un argumento real del tipo Button y del tipo MouseEventArgs (ambos subtipos de los parámetros formales Object y EventArgs, respectivamente):

Dim m As New MouseEventArgs(MouseButtons.Left, 2, 47, 11,0) 
OnClick(btn, m)

A la inversa, supongamos que podemos definir una subrutina RelaxedOnClick que toma dos parámetros Object y, a continuación, podemos llamarlo con argumentos reales del tipo Object y EventArgs:

Sub RelaxedOnClick(sender As Object, e As Object) Handles btn.Click
  MessageBox.Show("Hello World from" & btn.Text))
End Sub
Dim e As EventArgs = m
Dim s As Object = btn
RelaxedOnClick(btn,e)

En Visual Basic 9.0, se relaja el enlace a los delegados para ser coherente con el método de invocación. Es decir, si es posible invocar una función o subrutina con los argumentos reales que coinciden exactamente con los tipos de devolución y de parámetro formal, podemos enlazar esa función o subrutina al delegado. Dicho de otra forma, la definición y el enlace de los delegados seguirá la misma lógica de resolución de sobrecarga que sigue ese método.

Esto implica que en Visual Basic 9.0 ahora podemos enlazar una subrutina RelaxedOnClick que toma dos parámetros Object al evento Click de Button:

Sub RelaxedOnClick(sender As Object, e As Object) Handles btn.Click
  MessageBox.Show(("Hello World from" & btn.Text)
End Sub

Los dos argumentos al controlador de eventos, sender y EventArgs, rara vez son relevantes. En su lugar, el controlador tiene acceso al estado del control en el que el evento se registra directamente y omite sus dos argumentos. Para admitir este caso común, los delegados se pueden relajar para no tomar argumentos, si esto no provoca ambigüedades. Es decir, podemos escribir simplemente lo siguiente:

Sub RelaxedOnClick Handles btn.Click
  MessageBox.Show("Hello World from" & btn.Text)
End Sub

Se entiende que la relajación de delegados también se aplica a la creación de delegados mediante AddressOf o la expresión de creación de delegados, incluso cuando el grupo de métodos sea una llamada enlazada en tiempo de ejecución:

Dim F As EventHandler = AddressOf RelaxedOnClick
Dim G As New EventHandler(AddressOf btn.Click)

Conclusión

Visual Basic 9.0 unifica el acceso a los datos independientemente de su fuente en bases de datos relacionales, documentos XML o gráficos de objetos arbitrarios, ya sean mantenidos o almacenados en la memoria. La unificación consta de estilos, técnicas, herramientas y patrones de programación. La sintaxis especialmente flexible de Visual Basic simplifica el hecho de agregar al lenguaje extensiones como los literales XML y expresiones de consulta similares a las de SQL. Esto reduce en gran medida el "área de superficie" de las nuevas API de Consultas integradas en los lenguajes (Language Integrated Query, LINQ) de .NET, aumenta la detectabilidad de características de acceso a datos a través de Intellisense y Etiquetas Inteligentes, y mejora enormemente la depuración y comprobación del tiempo de compilación pasando las sintaxis ajenas de los datos de cadena a Visual Basic.

Asimismo, las características como la inferencia de tipos, los inicializadores de objeto y los delegados relajados reducen mucho la redundancia del código y el número de excepciones de las reglas que los programadores necesitan aprender y recordar o consultar, sin repercusión alguna en el rendimiento.

Aunque pueda parecer que la lista de características nuevas de Visual Basic 9.0 es larga, esperamos que los anteriores temas le convenzan de que es coherente y oportuna, y su objetivo es hacer de Visual Basic el mejor lenguaje de programación del mundo. Esperamos también haber estimulado su imaginación y que, como nosotros, sea consciente de que en realidad esto no es más que el principio y de que quedan cosas aún mejores por llegar.

¿Te ha resultado útil?
(Caracteres restantes: 1500)
Gracias por sus comentarios
Mostrar:
© 2014 Microsoft. Reservados todos los derechos.