Tipos anônimos (Visual Basic)

O Visual Basic dá suporte aos tipos anônimos, que permitem criar objetos sem escrever uma definição de classe para o tipo de dados. Em vez disso, o compilador gera uma classe para você. A classe não tem nenhum nome utilizável, herda diretamente de Object e contém as propriedades que você especificar ao declarar o objeto. Como o nome do tipo de dados não é especificado, ele é conhecido como um tipo anônimo.

O exemplo a seguir declara e cria a variável product como uma instância de um tipo anônimo que tem duas propriedades Name e Price.

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

Uma expressão de consulta usa tipos anônimos para combinar colunas de dados selecionadas por uma consulta. Você não pode definir o tipo do resultado com antecedência, pois não é possível prever as colunas que uma consulta específica pode selecionar. Tipos anônimos permitem que você escreva uma consulta que selecione qualquer número de colunas, em qualquer ordem. O compilador cria um tipo de dados que corresponde às propriedades especificadas e à ordem especificada.

Nos exemplos a seguir, products é uma lista de objetos de produto, cada um com muitas propriedades. A variável namePriceQuery contém a definição de uma consulta que, quando executada, retorna uma coleção de instâncias de um tipo anônimo que tem duas propriedades Name e Price.

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

A variável nameQuantityQuery contém a definição de uma consulta que, quando executada, retorna uma coleção de instâncias de um tipo anônimo que tem duas propriedades Name e OnHand.

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

Para obter mais informações sobre o código criado pelo compilador para um tipo anônimo, confira Definição de Tipo Anônimo.

Cuidado

O nome do tipo anônimo é gerado pelo compilador e pode variar de compilação para compilação. O seu código não deve usar nem depender do nome de um tipo anônimo porque o nome pode ser alterado quando um projeto é recompilado.

Declarar um tipo anônimo

A declaração de uma instância de um tipo anônimo usa uma lista de inicializadores para especificar as propriedades do tipo. Você só pode especificar propriedades quando declara um tipo anônimo, não outros elementos de classe, como métodos ou eventos. No exemplo a seguir, product1 é uma instância de um tipo anônimo que tem duas propriedades: Name e 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}

Se você designar propriedades como propriedades chave, você poderá usá-las para comparar duas instâncias de tipo anônimo para igualdade. No entanto, os valores das propriedades chave não podem ser alterados. Confira a seção Propriedades Chave mais adiante neste tópico para obter mais informações.

Observe que declarar uma instância de um tipo anônimo é como declarar uma instância de um tipo nomeado usando um inicializador de objeto:

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

Para obter mais informações sobre outras maneiras de especificar as propriedades de tipo anônimo, confira Como inferir nomes e tipos de propriedade em declarações de tipo anônimo.

Propriedades da chave

As propriedades chave diferem das propriedades não chave de várias maneiras fundamentais:

  • Somente os valores das propriedades chave são comparados para determinar se duas instâncias são iguais.

  • Os valores das propriedades chave são somente leitura e não podem ser alterados.

  • Somente valores de propriedade chave são incluídos no algoritmo de código hash gerado pelo compilador para um tipo anônimo.

Igualitário

Instâncias de tipos anônimos somente poderão ser iguais se forem instâncias do mesmo tipo anônimo. O compilador tratará duas instâncias como instâncias do mesmo tipo se elas atenderem às seguintes condições:

  • Elas são declaradas no mesmo assembly.

  • Suas propriedades têm os mesmos nomes, os mesmos tipos inferidos e são declaradas na mesma ordem. As comparações de nomes não diferenciam maiúsculas de minúsculas.

  • As mesmas propriedades em cada uma são marcadas como propriedades chave.

  • Pelo menos uma propriedade em cada declaração é uma propriedade chave.

Uma instância de um tipo anônimo que não tem propriedades chave é igual apenas a si mesma.

' 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))

Duas instâncias do mesmo tipo anônimo serão iguais se os valores de suas propriedades chave forem iguais. Os exemplos a seguir ilustram como a igualdade é testada.

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 somente leitura

Os valores das propriedades chave não podem ser alterados. Por exemplo, em prod8 no exemplo anterior, os campos Name e Price são read-only, mas OnHand pode ser alterado.

' 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 expressões de consulta

As expressões de consulta nem sempre exigem a criação de tipos anônimos. Quando possível, elas usam um tipo existente para manter os dados da coluna. Isso ocorre quando a consulta retorna registros inteiros da fonte de dados ou apenas um campo de cada registro. Nos exemplos de código a seguir, customers é uma coleção de objetos de uma classe Customer. A classe possui muitas propriedades e você pode incluir uma ou mais delas no resultado da consulta, em qualquer ordem. Nos dois primeiros exemplos, nenhum tipo anônimo é necessário, pois as consultas selecionam elementos de tipos nomeados:

  • custs1 contém uma coleção de cadeias de caracteres, pois cust.Name é uma cadeia de caracteres.

    Dim custs1 = From cust In customers
                 Select cust.Name
    
  • custs2 contém uma coleção de objetos Customer, pois cada elemento customers é um objeto Customer e todo o elemento é selecionado pela consulta.

    Dim custs2 = From cust In customers
                 Select cust
    

No entanto, nem sempre estão disponíveis os tipos nomeados apropriados. Talvez você queira selecionar nomes e endereços de clientes para uma finalidade, números de ID do cliente e locais para outro e nomes de clientes, endereços e históricos de pedidos para um terceiro. Tipos anônimos permitem que você selecione qualquer combinação de propriedades, em qualquer ordem, sem primeiro declarar um novo tipo nomeado para manter o resultado. Em vez disso, o compilador cria um tipo anônimo para cada compilação de propriedades. A consulta a seguir seleciona apenas o nome do cliente e o número de ID de cada objeto Customer em customers. Portanto, o compilador cria um tipo anônimo que contém apenas essas duas propriedades.

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

Ambos os nomes e os tipos de dados das propriedades no tipo anônimo são obtidos dos argumentos para Select, cust.Name e cust.ID. As propriedades em um tipo anônimo que é criado por uma consulta são sempre propriedades chave. Quando custs3 é executado no loop For Each a seguir, o resultado é uma coleção de instâncias de um tipo anônimo com duas propriedades chave, Name e ID.

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

Os elementos na coleção representada por custs3 tem tipos fortes e você pode usar o IntelliSense para navegar pelas propriedades disponíveis e verificar seus tipos.

Para obter mais informações, confira Introdução a LINQ em Visual Basic.

Decidir entre usar tipos anônimos

Antes de criar um objeto como uma instância de uma classe anônima, considere se essa é a melhor opção. Por exemplo, se você quiser criar um objeto temporário para conter dados relacionados e não precisar de outros campos e métodos que uma classe completa possa conter, um tipo anônimo é uma boa solução. Os tipos anônimos também são convenientes se você quiser uma seleção diferente de propriedades para cada declaração ou se você quiser alterar a ordem das propriedades. No entanto, se o projeto incluir vários objetos que possuem as mesmas propriedades, em uma ordem fixa, você poderá declará-los com mais facilidade usando um tipo nomeado com um construtor de classe. Por exemplo, com um construtor apropriado, é mais fácil declarar várias instâncias de uma classe Product do que declarar várias instâncias de um 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}

Outra vantagem dos tipos nomeados é que o compilador pode capturar um erro de digitação acidental de um nome de propriedade. Nos exemplos anteriores, firstProd2, secondProd2 e thirdProd2 se destinam a ser instâncias do mesmo tipo anônimo. No entanto, se você declarasse thirdProd2 acidentalmente de uma das seguintes maneiras, seu tipo seria diferente de firstProd2 e 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}

Mais importante, há limitações no uso de tipos anônimos que não se aplicam a instâncias de tipos nomeados. firstProd2, secondProd2 e thirdProd2 são instâncias do mesmo tipo anônimo. No entanto, o nome do tipo anônimo compartilhado não está disponível e não pode aparecer quando é esperado um nome de tipo no seu código. Por exemplo, um tipo anônimo não pode ser usado para definir uma assinatura de método, para declarar outra variável ou campo ou em qualquer declaração de tipo. Como resultado, os tipos anônimos não são apropriados quando você precisa compartilhar informações entre métodos.

Uma definição do tipo anônimo

Em resposta à declaração de uma instância de um tipo anônimo, o compilador cria uma nova definição de classe que contém as propriedades especificadas.

Se o tipo anônimo tiver pelo menos uma propriedade chave, a definição substituirá três membros herdados de Object: Equals, GetHashCode e ToString. O código produzido para testar a igualdade e determinar o valor do código hash considera apenas as propriedades chave. Se o tipo anônimo não tiver nenhuma propriedade chave, somente ToString será substituído. Propriedades explicitamente nomeadas de um tipo anônimo não podem entrar em conflito com esses métodos gerados. Ou seja, você não pode usar .Equals, .GetHashCode ou .ToString para nomear uma propriedade.

Definições de tipo anônimo que possuem pelo menos uma propriedade chave também implementam a interface System.IEquatable<T>, onde T é o tipo do tipo anônimo.

Para obter mais informações sobre o código criado pelo compilador e a funcionalidade dos métodos substituídos, confira Definição de Tipo Anônimo.

Confira também