Exportar (0) Imprimir
Expandir todo
Expandir Minimizar
Este tema aún no ha recibido ninguna valoración - Valorar este tema

Mejoras de lenguaje del nuevo Visual Basic 2005 (Hardcore Visual Studio .NET 2004)

18 de Julio de 2005

Publicado: Enero de 2005

Matthew MacDonald

Los desarrolladores de Microsoft están trabajando realmente duro para preparar una nueva versión de .NET, pero ¿qué sería una nueva plataforma de programación sin un ajuste del lenguaje? En este artículo, Matthew MacDonald describe los cambios que nos tiene preparados la última versión de Visual Basic, actualmente llamada Visual Basic 2005.

Puede que en el artículo se incluyan direcciones URL que fueron correctas en la fecha de publicación, pero que ahora hagan referencia a sitios o páginas inexistentes. Para conservar la fluidez del texto, se han mantenido estas direcciones en el texto, aunque se han desactivado los vínculos.

En esta página

Introducción Introducción
Sobrecarga de operadores  Sobrecarga de operadores
Clases parciales  Clases parciales
Instrucción Continue  Instrucción Continue
Instrucción Using  Instrucción Using
Accesibilidad dividida a las propiedades  Accesibilidad dividida a las propiedades
Componentes genéricos Componentes genéricos

Introducción

(Este artículo se basa en material del próximo libro de Matthew MacDonald, VB 2005 Developer's Notebook, de O'Reilly.)

El lanzamiento de .NET 1.0 consiguió revolucionar la manera en que los desarrolladores creaban aplicaciones modernas y cambió sustancialmente el lenguaje Visual Basic por primera vez en casi una década. Por ejemplo, tareas habituales como la creación de objetos, la declaración de estructuras, la presentación de formularios y el uso de matrices se modificaron considerablemente. Para ver una lista con los cambios de Visual Basic 6.0 a Visual Basic .NET, desde un punto de vista ligeramente contrariado, consulte www.mvps.org/vb/index2.html?rants/vfred.htm (en inglés).

Afortunadamente, .NET 2.0 no nos tiene reservados estos sustos. Por el contrario, éste refina el lenguaje respetando la funcionalidad de todo el código existente. Muchos de los cambios que encontrará duplican características del C#, aunque hay también nuevas características completamente nuevas para ambos lenguajes. En este artículo, veremos los cambios más útiles, entre los que se incluyen:

  • Sobrecarga de operadores

  • Clases parciales

  • Instrucción Continue

  • Instrucción Using

  • Accesibilidad dividida a las propiedades

  • Componentes genéricos

Una característica que no trataremos en este artículo es el nuevo objeto My, una especie de repositorio central de funcionalidad útil. El objeto My no representa realmente un cambio en el idioma Visual Basic, sino que es más bien un conjunto de accesos directos que se integran en VB. En próximos artículos veremos a fondo el objeto My.

Sobrecarga de operadores

Ya estará familiarizado con operadores aritméticos como los utilizados para la suma (+), la resta (-), la división (/) y la multiplicación (*). En VB.NET 1.0, estos operadores se aplicaban a tipos numéricos y, normalmente, no tenía ningún sentido aplicarlos a otros objetos. Sin embargo, no es así en C#, donde los desarrolladores pueden crear clases personalizadas que admitan todos estos operadores (y algunos más) para usarlos como deseen.

Como uno de los desarrolladores del compilador de VB.NET lo resumió, "la sobrecarga de operadores no se incluyó en VB.NET 1.0 ya que, desgraciadamente, no tuvimos suficiente tiempo para hacerlo". VB 2005 llena este vacío.

Para comprender cómo funciona la sobrecarga de operadores, consideremos un sencillo ejemplo. Examinemos el siguiente código, que no sería válido en VB.NET 1.0.

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

La línea importante es la antepenúltima, que utiliza el operador + para sumar dos objetos TimeSpan. El motivo por el que esto funciona es porque cuando el compilador analiza la expresión "ShortSpan + LongSpan", descubre que el tipo de ShortSpan es un TimeSpan y que TimeSpan define el operador + (que es sencillamente una función especial). Sabiendo esto, convierte el código en algo similar a esto (cuya sintaxis sería válida en VB.NET 1.0):

TotalSpan = ShortSpan.Add(LongSpan)

Para sobrecargar un operador en Visual Basic 2005, necesita crear un método de operador especial en su clase (o estructura). Este método debe declararse con las palabras clave Public Shared Operator, seguidas por el símbolo del operador (por ejemplo +). Por ejemplo, aquí vemos un método de operador que permite utilizar el operador de suma (+):

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

Todos los métodos de operadores aceptan dos parámetros, que representan los valores a cada lado del operador. Dependiendo de la clase y el operador, el orden puede ser importante (como ocurre con la división).

La sobrecarga de operadores puede no tener sentido para muchos objetos de negocios, pero es extremadamente práctica para modelar estructuras matemáticas como vectores, matrices, números complejos o fracciones. El listado 1 muestra cómo sobrecargar los operadores aritméticos de Visual Basic para poder sumar y restar un objeto Point básico (como el definido en los espacios de nombres GDI+ de .NET Framework).

Listado 1. Clase Point con sobrecarga de operadores

Public Class Point

    Public X As Integer
    Public Y As Integer

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

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

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

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

End Class

Normalmente, los parámetros y el valor de retorno de un método de operador utilizan el mismo tipo. Sin embargo, no hay ningún motivo por el que no pueda crear más de una versión de un método de operador (sobrecarga) de manera que el objeto se pueda utilizar en expresiones con tipos diferentes.

Clases parciales

Las clases parciales no resultan tan atractivas como la sobrecarga de operadores, pero son igualmente útiles. Con ellas, puede dividir una clase en varios archivos. Basta con definir la misma clase en más de un sitio y asegurarse de incluir la palabra clave Partial. A continuación, vemos un ejemplo que define una clase denominada TestClass en dos partes:

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

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

En este ejemplo, las dos declaraciones se encuentran en el mismo archivo, una tras otra. Sin embargo, no hay ningún motivo por el que no pueda colocar las dos partes de TestClass en diferentes archivos de código origen del mismo proyecto. (Las únicas restricciones son que no se pueden definir las dos partes en ensamblados diferentes ni en espacios de nombres diferentes.) Tampoco hay ningún motivo por el que no se pueda dividir TestClass en más partes, ya que básicamente, no hay límites.

Al generar la aplicación, Visual Studio .NET realiza un seguimiento de cada parte de TestClass y las ensambla en una clase compilada y completa con dos métodos TestMethodA() y TestMethodB(). Puede utilizar ambos métodos de la misma manera, como se muestra a continuación:

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

En general, las clases parciales sirven para dos objetivos:

  • Pueden ayudar a dividir clases extremadamente grandes en partes más manejables.

  • Puede ocultar código que no sea importante, como el código generado automáticamente por el diseñador.

La segunda utilidad es realmente la más importante y es el motivo por el que se han incorporado las clases parciales en lenguajes .NET como VB 2005 y C#. Por ejemplo, al generar un formulario .NET en Visual Studio 2005, el código que administra los eventos se coloca en el archivo de código fuente correspondiente al formulario, pero el código del diseñador que crea y configura cada control y conecta los controladores de eventos no resulta visible, ya que el código se coloca en un archivo diferente y no se deja visible para que no se modifique por error. (Por supuesto, puede ver este código si desea asegurarse de que realmente está ahí. Sólo tiene que seleccionar Project | Show All Files en el menú de Visual Studio. Al hacerlo, aparecerá un nuevo archivo con la otra mitad de la clase. Por ejemplo, si crea un nuevo formulario denominado Form1, en realidad obtendrá finalmente un archivo Form1.vb para el código y un archivo Form1.Designer.vb con la parte generada automáticamente. Una última advertencia: esto no se ha implementado todavía en todas las primeras versiones alfa de Visual Studio 2005.)

Instrucción Continue

El lenguaje Visual Basic tiene varias instrucciones de control del flujo que permiten dirigir la ejecución. Por ejemplo, puede utilizar Return para salir de una función o Exit para saltar de un bucle. Sin embargo, hasta VB 2005, no había ninguna manera de continuar con la siguiente iteración de un bucle. VB 2005 agrega este ingrediente que faltaba gracias a la nueva instrucción Continue.

Continue es uno de los detalles del lenguaje que parece trivial al principio, pero que rápidamente se ve que es extremadamente cómodo. Hay tres versiones de la instrucción Continue (Continue For, Continue Do y Continue While) cada una de ellas destinada a ser utilizada con un tipo diferente de bucle (For ... Next, Do ... Loop o While ... End While).

Para ver cómo funciona la instrucción Continue, examinemos este código:

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

Este código se repite 1.000 veces, incrementando un contador i. Siempre que es divisible por 5, se realiza el código correspondiente a la tarea A. Seguidamente, se ejecuta la instrucción Continue For, se incrementa el contador y la ejecución continúa desde el principio del bucle, saltándose el código correspondiente a la tarea B.

En este ejemplo, la instrucción Continue no es realmente necesaria, porque se podría reescribir el código utilizando una lógica condicional más sofisticada. Sin embargo, si está trabajando con una confusa sección de código en la que es necesario realizar varias pruebas diferentes, resulta muy práctico utilizar la instrucción Continue para salir rápidamente.

Una limitación de la instrucción Continue es que tal vez no funcione de la manera deseada en bucles anidados. Si anida un bucle dentro de otro bucle del mismo tipo, no hay ninguna manera que no sea ambigua de referirse al bucle exterior, por lo que la instrucción Continue siempre se referirá al bucle interior.

Sin embargo, no habrá ningún problema si hay dos tipos diferentes de bucles. Por ejemplo, si anida un bucle For dentro de un bucle Do, podrá utilizar Continue For para saltarse la siguiente iteración del bucle interior o bien Continue Do para saltarse la siguiente iteración del bucle exterior, como se muestra a continuación:

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

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

Esta técnica también funciona al revés (con un bucle Do dentro de un bucle For).

Instrucción Using

En .NET, es extremadamente importante asegurarse de que los objetos que utilizan recursos no administrados (como identificadores de archivos, conexiones de base de datos, contextos gráficos, etc.) liberan dichos recursos lo antes posible. Con este objetivo, estos objetos siempre implementan la interfaz IDisposable y ofrecen un método Dispose() que permite liberar inmediatamente sus recursos.

El único problema es que es necesario acordarse de llamar al método Dispose() (u otro método que llame a Dispose(), como un método Close()). VB 2005 proporciona una nueva manera de asegurarse de llamar siempre a Dispose(): la instrucción Using.

La instrucción Using se utiliza en una estructura de bloque. En la primera línea, al declarar el bloque Using, se especifica el objeto desechable que se está utilizando. Normalmente, también creará el objeto al mismo tiempo utilizando la palabra clave New. A continuación, se escribe el código que utiliza el objeto desechable dentro del bloque Using. Seguidamente, se incluye un ejemplo con un fragmento de código que crea un nuevo archivo y escribe en él algunos datos:

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

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

En este ejemplo, en cuanto la ejecución abandona el bloque Using, se llama al método Dispose() para el objeto NewFile, lo que libera el identificador del archivo. Afortunadamente, .NET se asegura de que se desecha el recurso independientemente de cómo se salga del bloque Using, incluso si se produce una excepción no controlada.

Accesibilidad dividida a las propiedades

VB reconoce tres niveles de accesibilidad, que son los siguientes (ordenados del más permisivo al menos):

  • Public (disponible para todas las clases en todos los ensamblados)

  • Friend (disponible para todo el código de las clases del ensamblado actual)

  • Private (disponible únicamente para el código de la misma clase)

Imaginemos que está creando un componente DLL que va a utilizar otra aplicación. Tal vez decida crear una propiedad denominada Status que la aplicación cliente necesita leer y, por tanto, puede hacer la propiedad Public:

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

En este ejemplo, los procedimientos set y get de la propiedad tienen la misma accesibilidad, tal como exige VB.NET 1.0. El problema aquí es que el cliente puede cambiar la propiedad Status, lo que no tiene sentido. Podría imponer que la propiedad fuera de sólo lectura (es decir, omitir completamente el procedimiento set de la propiedad), pero en ese caso las demás clases del ensamblado componente no podrían cambiar el valor de Status.

La solución que ofrece VB 2005 consiste en darle al procedimiento set de la propiedad el nivel de accesibilidad Friend. Veamos el aspecto resultante del código:

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

Componentes genéricos

Uno de los principios básicos de la programación orientada a objetos consiste en hacer las soluciones lo más abstractas y generales posibles, para que se puedan reutilizar en varios escenarios. Desgraciadamente, no siempre es así de sencillo. Uno de los retos a los que se enfrentan los programadores consiste en crear clases que sean suficientemente genéricas para ser reutilizadas, pero al mismo tiempo suficientemente específicas para detectar errores y lograr un buen rendimiento.

Por ejemplo, consideremos las colecciones. Un buen programador orientado a objetos preferiría generar una clase genérica Collection que se pudiera utilizar con cualquier tipo de objeto, en vez de una colección distinta para cada tipo concreto de elemento (como OrderItemCollection). Evidentemente, la clase genérica Collection es más sencilla, más elegante y supone mucho menos trabajo que un conjunto de clases personalizadas como OrderItemCollection. Por otra parte, las consideraciones acerca del rendimiento y la seguridad de tipos hacen que la solución genérica resulte menos atractiva. Es decir, si se utiliza una clase genérica Collection para almacenar objetos OrderItem, ¿cómo podemos asegurarnos de que alguien no insertará posteriormente otro tipo de objetos, lo que produciría molestos problemas?

.NET 2.0 ofrece una solución denominada componentes genéricos. Los componentes genéricos son clases que están parametrizadas por tipo. Es decir, se crea una plantilla de clase que admite todos los tipos. Al crear una instancia de dicha clase, se especifica el tipo que se desea utilizar y, a partir de ese momento, el objeto queda ligado inevitablemente al tipo elegido. Los componentes genéricos se incluyen en Common Language Runtime, por lo que estarán disponibles en todos los lenguajes compatibles, incluidos VB y C#. (Para obtener más información sobre la implementación en C# de los componentes genéricos, consulte el artículo de Joe Mayo sobre este tema en este mismo número.)

Otro ejemplo de aplicación muy útil de los componentes genéricos lo encontramos en la clase System.Collections.ArrayList. ArrayList es una colección de tamaño ajustado de forma automática y dinámica de uso general. Puede incluir los objetos habituales de .NET o sus propios objetos personalizados. Para ello, ArrayList lo trata todo como si correspondiera al tipo base Object. El problema está en que no hay manera de imponer ninguna restricción al funcionamiento de ArrayList. Por ejemplo, si desea utilizar ArrayList para almacenar una colección de objetos Customer, no hay manera de asegurarse de que un fragmento erróneo de código no inserte por error cadenas, enteros o cualquier otro tipo de objeto, lo que podría suponerle en el futuro un quebradero de cabeza. Por este motivo, los desarrolladores con frecuencia crean sus propias clases de colecciones con tipos inflexibles. De hecho, podemos encontrar docenas de ellas en la biblioteca de clases .NET.

Los genéricos pueden resolver este problema. Por ejemplo, mediante los componentes genéricos es posible declarar una clase que funcione con cualquier tipo mediante la palabra clave Of:

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

En este caso, se crea una nueva clase llamada GenericList que puede funcionar con cualquier tipo de objeto. Sin embargo, el cliente necesita especificar el tipo que se utilizará. En el código de la clase, se hace referencia a dicho tipo como ItemType. Por supuesto, ItemType no es realmente un tipo, sino sencillamente un marcador de posición para el tipo que se elegirá al crear un objeto GenericList. El listado 2 muestra el código completo para una ArrayList con seguridad de tipos.

Listado 2. Una colección con seguridad de tipos mediante componentes genéricos

Public Class GenericList(Of ItemType)
    Inherits CollectionBase

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

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

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

La clase GenericList envuelve una ArrayList habitual. Sin embargo, ofrece métodos Add() y Remove() de tipos inflexibles, que utilizan el marcador de posición ItemType.

A continuación, vemos un ejemplo de cómo se puede utilizar la clase GenericList para crear una colección ArrayList que sólo admita cadenas:

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

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

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

El número de maneras de parametrizar una clase no tiene límites. En el ejemplo GenericList, hay únicamente un parámetro de tipo. Sin embargo, se puede crear con facilidad una clase que funcione con dos o tres tipos de objetos y permita que todos estos tipos sean genéricos. Para utilizar este enfoque, basta con separar cada tipo con una coma (entre los corchetes angulares que hay al principio de una clase).Por ejemplo, veamos la siguiente clase GenericHashTable, que permite definir el tipo que se desea utilizar para los elementos que se almacenan (ItemType) y las claves que se utilizan para indizar los tipos (KeyType).

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

Otra característica importante de los componentes genéricos es la posibilidad de utilizar restricciones, lo que permite limitar los tipos admitidos. Por ejemplo, tal vez quiera crear una clase que admita cualquier tipo que cumpla una determinada interfaz. En este caso, tras declarar el marcador de posición de tipo, agregue la palabra clave As seguida por la clase base o la interfaz que debe tener el tipo.

A continuación, mostraremos un ejemplo que limita la clase GenericList de manera que sólo puede utilizar elementos serializables. (Puede que quiera utilizar este enfoque porque desea agregar otro método en GenericList que requiera serialización, como un método que escriba todos los elementos en una secuencia.)

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

Puede definir tantas restricciones como desee, siempre que separe cada clase o interfaz con el símbolo &. El compilador obliga a que se cumplan estas restricciones.

Por cierto, los diseñadores .NET Framework saben bien lo útiles que resultan las colecciones genéricas, por lo que han creado varias que puede utilizar. Las encontrará en el nuevo espacio de nombres Systems.Collections.Generic, que incluye:

  • List: colección básica como el ejemplo GenericList.

  • Dictionary: colección nombre-valor que indiza cada elemento con una clave.

  • LinkedList: lista vinculada, en la que cada elemento apunta al siguiente de la cadena.

  • Queue: colección FIFO.

  • Stack: colección LIFO.

  • ReadOnlyCollection: colección con un conjunto fijo de elementos que no se pueden cambiar una vez que se crean.

  • SortedDictionary: colección nombre-valor que se mantiene siempre ordenada.

La mayoría de estos tipos duplican los del espacio de nombres System.Collections. Las colecciones anteriores se conservan para mantener la compatibilidad con versiones anteriores.

Conclusión

Como hemos visto en este artículo, los cambios del lenguaje de la última versión de VB son mejoras que hacen la vida más fácil sin hacer que ningún código existente sea obsoleto. La mayoría de estos cambios son características menores del idioma importadas de C# (como la sobrecarga de operadores), mientras que otras son ingredientes completamente nuevos que se han incorporado a la última versión de Common Language Runtime (como los componentes genéricos). Evidentemente, el lenguaje VB está vivo y bien, y sigue evolucionando a algo mejor.

Para obtener más información acerca de Hardcore Visual Studio y Pinnacle Publishing, visite su sitio Web en http://www.pinpub.com/ (en inglés)

Nota: este sitio Web no pertenece a Microsoft Corporation. Microsoft no se responsabiliza de su contenido.

Este artículo es una reproducción del incluido en el número de noviembre de 2004 de Hardcore Visual Studio. Copyright 2004, por Pinnacle Publishing, Inc., salvo que se especifique de otro modo. Reservados todos los derechos. Hardcore Visual Studio es una publicación producida de manera independiente por Pinnacle Publishing, Inc. No se podrá utilizar ni reproducir parte alguna de este artículo de ninguna manera (salvo las citas breves utilizadas en las reseñas y los artículos críticos) sin el previo consentimiento por escrito de Pinnacle Publishing, Inc. Si desea ponerse en contacto con Pinnacle Publishing, Inc., llame al 1-800-788-1900.

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