Tipos anónimos (Visual Basic)

Visual Basic es compatible con los tipos anónimos, que permiten crear objetos sin escribir una definición de clase para el tipo de datos. En su lugar, el compilador genera una clase. La clase no tiene ningún nombre que se pueda usar, hereda directamente de Object y contiene las propiedades especificadas al declarar el objeto. Dado que no se especifica el nombre del tipo de datos, se conoce como un tipo anónimo.

En el ejemplo siguiente se declara y se crea una variable product como una instancia de un tipo anónimo que tiene dos propiedades, Name y Price.

' Variable product is an instance of a simple anonymous type.
Dim product = New With {Key .Name = "paperclips", .Price = 1.29}

Una expresión de consulta usa tipos anónimos para combinar columnas de datos seleccionados mediante una consulta. No se puede definir el tipo del resultado de antemano, ya que no se pueden predecir las columnas que puede seleccionar una consulta determinada. Los tipos anónimos permiten escribir una consulta que selecciona cualquier número de columnas, en cualquier orden. El compilador crea un tipo de datos que coincide con las propiedades especificadas y el orden indicado.

En los ejemplos siguientes, products es una lista de objetos de producto, cada uno de los cuales tiene muchas propiedades. La variable namePriceQuery contiene la definición de una consulta que, cuando se ejecuta, devuelve una colección de instancias de un tipo anónimo que tiene dos propiedades, Name y Price.

Dim namePriceQuery = From prod In products
                     Select prod.Name, prod.Price

La variable nameQuantityQuery contiene la definición de una consulta que, cuando se ejecuta, devuelve una colección de instancias de un tipo anónimo que tiene dos propiedades, Name y OnHand.

Dim nameQuantityQuery = From prod In products
                        Select prod.Name, prod.OnHand

Para obtener más información sobre el código creado por el compilador para un tipo anónimo, vea Definición de tipos anónimos.

Precaución

El nombre del tipo anónimo lo genera el compilador y puede variar de compilación a compilación. El código no debe usar el nombre de un tipo anónimo, ni basarse en él, porque el nombre podría cambiar al volver a compilar un proyecto.

Declaración de un tipo anónimo

La declaración de una instancia de un tipo anónimo usa una lista de inicializadores para especificar las propiedades del tipo. Solo se pueden especificar propiedades al declarar un tipo anónimo, no otros elementos de clase, como métodos o eventos. En el ejemplo siguiente, product1 es una instancia de un tipo anónimo que tiene dos propiedades: Name y Price.

' Variable product1 is an instance of a simple anonymous type.
Dim product1 = New With {.Name = "paperclips", .Price = 1.29}
' -or-
' product2 is an instance of an anonymous type with key properties.
Dim product2 = New With {Key .Name = "paperclips", Key .Price = 1.29}

Si se designan propiedades como propiedades clave, pueden usarse para comparar dos instancias de tipo anónimo para ver si son iguales. Pero no se pueden cambiar los valores de las propiedades clave. Vea la sección Propiedades clave más adelante en este tema para obtener más información.

Observe que declarar una instancia de un tipo anónimo es como declarar una instancia de un tipo con nombre mediante un inicializador de objeto:

' Variable product3 is an instance of a class named Product.
Dim product3 = New Product With {.Name = "paperclips", .Price = 1.29}

Para obtener más información sobre otras formas de especificar propiedades de tipo anónimo, vea Procedimiento para deducir tipos y nombres de propiedades en declaraciones de tipos anónimos.

Propiedades clave

Las propiedades clave difieren de las propiedades que no son clave en varios aspectos fundamentales:

  • Solo se comparan los valores de las propiedades clave para determinar si dos instancias son iguales.

  • Los valores de las propiedades clave son de solo lectura y no se pueden cambiar.

  • Solo se incluyen los valores de propiedades clave en el algoritmo de código hash generado por el compilador de un tipo anónimo.

Igualdad

Las instancias de tipos anónimos solo pueden ser iguales si son instancias del mismo tipo anónimo. El compilador trata dos instancias como instancias del mismo tipo si cumplen las condiciones siguientes:

  • Se declaran en el mismo ensamblado.

  • Sus propiedades tienen los mismos nombres, los mismos tipos deducidos y se declaran en el mismo orden. Las comparaciones de nombres no distinguen mayúsculas de minúsculas.

  • Las mismas propiedades de cada una están marcadas como propiedades clave.

  • Al menos una propiedad de cada declaración es una propiedad clave.

Una instancia de un tipo anónimo que no tiene propiedades clave solo es igual a sí misma.

' prod1 and prod2 have no key values.
Dim prod1 = New With {.Name = "paperclips", .Price = 1.29}
Dim prod2 = New With {.Name = "paperclips", .Price = 1.29}

' The following line displays False, because prod1 and prod2 have no
' key properties.
Console.WriteLine(prod1.Equals(prod2))

' The following statement displays True because prod1 is equal to itself.
Console.WriteLine(prod1.Equals(prod1))

Dos instancias del mismo tipo anónimo son iguales si los valores de sus propiedades clave son iguales. En los ejemplos siguientes se muestra cómo se comprueba la igualdad.

Dim prod3 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim prod4 = New With {Key .Name = "paperclips", Key .Price = 1.29}
' The following line displays True, because prod3 and prod4 are
' instances of the same anonymous type, and the values of their
' key properties are equal.
Console.WriteLine(prod3.Equals(prod4))

Dim prod5 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim prod6 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 423}
' The following line displays False, because prod5 and prod6 do not 
' have the same properties.
Console.WriteLine(prod5.Equals(prod6))

Dim prod7 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 24}
Dim prod8 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 423}
' The following line displays True, because prod7 and prod8 are
' instances of the same anonymous type, and the values of their
' key properties are equal. The equality check does not compare the
' values of the non-key field.
Console.WriteLine(prod7.Equals(prod8))

Valores de solo lectura

Los valores de las propiedades clave no se pueden cambiar. Por ejemplo, en prod8, el ejemplo anterior, los campos Name y Price son read-only, pero OnHand se puede cambiar.

' The following statement will not compile, because Name is a key
' property and its value cannot be changed.
' prod8.Name = "clamps"

' OnHand is not a Key property. Its value can be changed.
prod8.OnHand = 22

Tipos anónimos de expresiones de consulta

Las expresiones de consulta no siempre exigen la creación de tipos anónimos. Siempre que es posible, usan un tipo existente para contener los datos de columna. Esto ocurre cuando la consulta devuelve registros completos del origen de datos o solo un campo de cada registro. En los ejemplos de código siguientes, customers es una colección de objetos de una clase Customer. La clase tiene muchas propiedades y es posible incluir una o varias de ellas en el resultado de la consulta, en cualquier orden. En los dos primeros ejemplos no se requieren tipos anónimos porque las consultas seleccionan elementos de tipos con nombre:

  • custs1 contiene una colección de cadenas, porque cust.Name es una cadena.

    Dim custs1 = From cust In customers
                 Select cust.Name
    
  • custs2 contiene una colección de objetos Customer, porque cada elemento de customers es un objeto Customer, y la consulta selecciona todo el elemento.

    Dim custs2 = From cust In customers
                 Select cust
    

Pero no siempre hay tipos con nombre adecuados disponibles. Es posible que se quieran seleccionar nombres y direcciones de clientes para un propósito, números de identificación de clientes y ubicaciones para otro, y nombres de clientes, direcciones e historiales de pedidos para un tercero. Los tipos anónimos permiten seleccionar cualquier combinación de propiedades, en cualquier orden, sin necesidad de declarar primero un nuevo tipo con nombre para contener el resultado. En su lugar, el compilador crea un tipo anónimo para cada compilación de propiedades. La consulta siguiente selecciona solo el nombre del cliente y el número de identificación de cada objeto Customer de customers. Por lo tanto, el compilador crea un tipo anónimo que contiene solo esas dos propiedades.

Dim custs3 = From cust In customers
             Select cust.Name, cust.ID

Tanto los nombres como los tipos de datos de las propiedades del tipo anónimo se toman de los argumentos para Select, cust.Name y cust.ID. Las propiedades de un tipo anónimo creado por una consulta siempre son propiedades clave. Cuando custs3 se ejecuta en el siguiente bucle For Each, el resultado es una colección de instancias de un tipo anónimo con dos propiedades clave, Name e ID.

For Each selectedCust In custs3
    Console.WriteLine(selectedCust.ID & ": " & selectedCust.Name)
Next

Los elementos de la colección representados por custs3 están fuertemente tipados, y se puede usar IntelliSense para navegar por las propiedades disponibles y comprobar sus tipos.

Para más información, vea Introducción a LINQ en Visual Basic.

Decisión de usar tipos anónimos

Antes de crear un objeto como una instancia de una clase anónima, piense si esa es la mejor opción. Por ejemplo, si se quiere crear un objeto temporal para contener datos relacionados y no se necesitan otros campos ni métodos que pueda contener una clase completa, un tipo anónimo es una buena solución. Los tipos anónimos también son adecuados si se quiere una selección diferente de propiedades para cada declaración, o si se quiere cambiar el orden de las propiedades. Pero si el proyecto incluye varios objetos que tienen las mismas propiedades, en un orden fijo, se pueden declarar más fácilmente mediante un tipo con nombre con un constructor de clase. Por ejemplo, con un constructor adecuado, es más fácil declarar varias instancias de una clase Product que declarar varias instancias de un tipo anónimo.

' Declaring instances of a named type.
Dim firstProd1 As New Product("paperclips", 1.29)
Dim secondProd1 As New Product("desklamp", 28.99)
Dim thirdProd1 As New Product("stapler", 5.09)

' Declaring instances of an anonymous type.
Dim firstProd2 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim secondProd2 = New With {Key .Name = "desklamp", Key .Price = 28.99}
Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = 5.09}

Otra ventaja de los tipos con nombre es que el compilador puede detectar un error de escritura accidental de un nombre de propiedad. En los ejemplos anteriores, firstProd2, secondProd2 y thirdProd2 están diseñados para ser instancias del mismo tipo anónimo. Pero si se declarara thirdProd2 accidentalmente de una de las maneras siguientes, su tipo sería diferente del de firstProd2 y secondProd2.

' Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = 5.09}
' Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = "5.09"}
' Dim thirdProd2 = New With {Key .Name = "stapler", .Price = 5.09}

Lo más importante es que hay limitaciones en cuanto al uso de tipos anónimos que no se aplican a las instancias de tipos con nombre. firstProd2, secondProd2 y thirdProd2 son instancias del mismo tipo anónimo. Pero el nombre del tipo anónimo compartido no está disponible y no puede aparecer donde se espera un nombre de tipo en el código. Por ejemplo, un tipo anónimo no se puede usar para definir una firma de método, para declarar otra variable o campo, o en cualquier declaración de tipo. Así, los tipos anónimos no son adecuados cuando hay que compartir información entre métodos.

Definición de un tipo anónimo

En respuesta a la declaración de una instancia de un tipo anónimo, el compilador crea una nueva definición de clase que contiene las propiedades especificadas.

Si el tipo anónimo contiene al menos una propiedad clave, la definición invalida tres miembros heredados de Object: Equals, GetHashCode y ToString. El código generado para comprobar la igualdad y determinar el valor del código hash solo tiene en cuenta las propiedades clave. Si el tipo anónimo no contiene propiedades clave, solo se invalida ToString. Las propiedades con nombre explícito de un tipo anónimo no pueden entrar en conflicto con estos métodos generados. Es decir, no se puede usar .Equals, .GetHashCode o .ToString para asignar un nombre a una propiedad.

Las definiciones de tipo anónimo que tienen al menos una propiedad clave también implementan la interfaz System.IEquatable<T>, donde T es el tipo del tipo anónimo.

Para obtener más información sobre el código creado por el compilador y la funcionalidad de los métodos invalidados, vea Definición de tipos anónimos.

Consulte también