Tipos anônimos (Visual Basic)

Visual Basic oferece suporte a tipos anônimos, que permitem que você crie 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 nome utilizável, herda diretamente de Objecte contém as propriedades especificadas ao declarar o objeto. Como o nome do tipo de dados não é especificado, ele é chamado de um tipo anônimo.

O exemplo a seguir declara e cria 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. Não é possível definir o tipo de resultado com antecedência, porque não é possível prever as colunas que uma consulta específica pode selecionar. Os tipos anônimos permitem que você escreva uma consulta que seleciona 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 dos quais tem muitas propriedades. 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

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, consulte Definição de tipo anônimo.

Atenção

O nome do tipo anônimo é gerado pelo compilador e pode variar de compilação para compilação. Seu código não deve usar ou confiar no nome de um tipo anônimo porque o nome pode mudar quando um projeto é recompilado.

Declarando 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ê pode especificar apenas 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 de chave, poderá usá-las para comparar duas instâncias de tipo anônimo para igualdade. No entanto, os valores das propriedades de chave não podem ser alterados. Consulte a seção Propriedades da 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 propriedades de tipo anônimo, consulte Como inferir nomes e tipos de propriedade em declarações de tipo anônimo.

Propriedades-chave

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

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

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

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

Equality

As instâncias de tipos anónimos só podem ser iguais se forem instâncias do mesmo tipo anónimo. O compilador trata duas instâncias como instâncias do mesmo tipo se elas atenderem às seguintes condições:

  • São declarados na mesma assembleia.

  • 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 principais.

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

Uma instância de um tipo anônimo que não tem propriedades de 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 são iguais se os valores de suas propriedades de chave forem iguais. Os exemplos que se seguem ilustram a forma 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 de chave não podem ser alterados. Por exemplo, no prod8 exemplo anterior, os Name campos e Price são read-only, mas OnHand podem ser alterados.

' 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, eles usam um tipo existente para armazenar 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 Customer classe. A classe tem 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 porque as consultas selecionam elementos de tipos nomeados:

  • custs1 contém uma coleção de strings, porque cust.Name é uma string.

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

    Dim custs2 = From cust In customers
                 Select cust
    

No entanto, os tipos nomeados apropriados nem sempre estão disponíveis. Talvez você queira selecionar nomes e endereços de clientes para uma finalidade, números de ID de cliente e locais para outra e nomes, endereços e históricos de pedidos de clientes para uma terceira. Os tipos anônimos permitem que você selecione qualquer combinação de propriedades, em qualquer ordem, sem primeiro declarar um novo tipo nomeado para armazenar 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 e o número de ID do cliente de cada Customer objeto no 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

Os nomes e os tipos de dados das propriedades no tipo anônimo são retirados 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 de chave. Quando custs3 é executado no loop a seguir For Each , o resultado é uma coleção de instâncias de um tipo anônimo com duas propriedades Name de chave e ID.

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

Os elementos na coleção representados por custs3 são fortemente tipados e você pode usar o IntelliSense para navegar pelas propriedades disponíveis e verificar seus tipos.

Para obter mais informações, consulte Introdução ao LINQ no Visual Basic.

Decidindo se deseja 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ê deseja criar um objeto temporário para conter dados relacionados e não precisa de outros campos e métodos que uma classe completa pode 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 quiser alterar a ordem das propriedades. No entanto, se seu projeto inclui vários objetos que têm as mesmas propriedades, em uma ordem fixa, você pode declará-los mais facilmente 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 Product classe 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 detetar um erro acidental de digitação de um nome de propriedade. Nos exemplos anteriores, firstProd2, secondProd2, e thirdProd2 destinam-se a ser instâncias do mesmo tipo anônimo. No entanto, se você declarasse acidentalmente thirdProd2 de uma das seguintes maneiras, seu tipo seria diferente do 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 ainda, há limitações no uso de tipos anônimos que não se aplicam a instâncias de tipos nomeados. firstProd2, secondProd2e thirdProd2 são instâncias do mesmo tipo anônimo. No entanto, o nome para o tipo anônimo compartilhado não está disponível e não pode aparecer onde um nome de tipo é esperado em 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 de 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 contiver pelo menos uma propriedade de 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 da chave. Se o tipo anônimo não contiver propriedades de 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, .GetHashCodeou .ToString nomear uma propriedade.

As definições de tipo anônimo que têm pelo menos uma propriedade de chave também implementam a System.IEquatable<T> interface, 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, consulte Definição de tipo anônimo.

Consulte também