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, porquecust.Name
é uma string.Dim custs1 = From cust In customers Select cust.Name
custs2
Contém uma coleção deCustomer
objetos, porque cada elemento decustomers
é umCustomer
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
, secondProd2
e 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
, .GetHashCode
ou .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.