LINQ -  Tipos Anônimos (revisão)


E cá estou novamente a falar sobre LINQ dessa vez para dar um visão geral sobre os principais conceitos e também revendo e detalhando conceitos importantes sobre LINQ que se bem entendidos poderão nos ajudar muito nas tarefas do dia a dia.

O foco do artigo de hoje serão os tipos anônimos que permitem a criação de objetos sem escrever um definição de classe para o tipo de dados. Em vez disso, o compilador gera uma classe para você. A classe não possui um nome utilizável, herda diretamente de Object e contém as propriedades que você especifica ao declarar o objeto.

Porque não foi especificado o nome do tipo de dados, ele é conhecido como um tipo anônimo.

Recordando um pouco sobre o LINQ

A LINQ - Language Integrated Query é um novo recurso da plataforma .NET, que estende as capacidades de consulta, usando C # e Visual Basic. Ela esta disponível tanto no Visual Studio como nas versões Express e vem com assemblies de provedor de LINQ que permitem o seu uso com diferentes fontes de dados como coleções de memória, banco de dados relacionais , Datasets, documentos XML e outras fontes de dados.

Tanto no Visual Studio como nas versões Express as linguagens Visual C # e Visual Basic implementam as extensões da linguagem LINQ; essas extensões da linguagem LINQ usam a nova API Standard Query Operators, que é a linguagem de consulta para qualquer coleção que implementa a interface IEnumerable<T>. Isso significa que todas as coleções e arrays podem ser consultadas usando LINQ. As classes de coleções precisam apenas implementar a interface IEnumerable<T>, para habilitá-lo a usar a LINQ para consultar as coleções.

A Standard Query Operador (Operadores de consulta padrão) é uma API que permite a consulta de qualquer array. ou coleção .NET. Essa API consiste em os métodos declarados na classe estática System.Query.Sequence localizada no assembly System.Query.dll.

A figura abaixo mostra a arquitetura da LINQ que pode consultar diferentes tipos de dados usando diferentes linguagens:

LINQ to Objects Usado para acessar estruturas de dados na memória. Consulta qualquer tipo que suporta IEnumerable (Of T) (Visual Basic) ou IEnumerable<T> (C #).
LINQ to SQL Usado para gerenciar os dados relacionais como objetos. É parte da família de tecnologias ADO.NET. Traduz a consultas LINQ no modelo de objeto para SQL e as envia ao banco de dados para execução. Quando o banco de dados retorna o resultado, A LINQ to SQL os traduz de volta para os objetos com o qual podemos trabalhar. A LINQ para SQL também suporta os procedimentos armazenados e funções definidas pelo usuário no banco de dados.
LINQ to DataSets Facilita a consulta dos dados armazenados em cache em DataSet.Um DataSet é um conjunto de dados desconectado e consolidado a partir de fontes de dados diferentes.
LINQ to Entities O Entity Data Model é um modelo conceitual de dados que podem ser usado para modelar os dados de forma que as aplicações podem interagir com dados como entidades ou objetos. Através do Modelo de Dados Entidade, ADO.NET expõe as entidades como objetos.
LINQ to XML Fornece as capacidades para modificação de documento em memória do DOM - Document Object Model e suporta consultas LINQ. Com LINQ to XML você pode consultar, modificar, navegar e salvar as alterações de um documento XML. Ele permite escrever consultas para navegar e recuperar uma coleção de documentos e atributos sendo semelhante ao XPath e XQuery.

Dentre os recursos que foram implementado para dar suporte ao LINQ os tipos anônimos cumprem um papel importante.

Tipos anônimos (Anonymous Types)

Os Tipos anônimos são usados para definir tipos fortemente tipados sem ter que definir a classe completa.

Os Tipos anônimos são fortemente tipados e verificados em tempo de compilação. Este tipo é amplamente utilizado pela LINQ, porque a LINQ retorna dados dinamicamente formatados, cujo tipo é determinado pela consulta LINQ. Na LINQ, os tipos são definidos em situações em que os tipos são necessários , ou apenas uma vez.

Por exemplo, dado um produto que possui propriedades como nome, cor, ingredientes, preço e valor, poderíamos, por vezes, só precisar do nome e do preço do produto. Os tipos anônimos nos ajudam a definir um tipo dinâmico que contém somente o nome e preço do objeto produto. Isto também é chamado moldar o resultado dos dados que está sendo consultado em uma estrutura ou formato diferente da fonte de dados original.

Vejamos um exemplo:

Vamos definir uma classe Pizza conforme abaixo:

Namespace Macoratti

    Public Class Pizza

        Public nome As String
        Public ingredientes As String
        Public gordura As String
        Public colesterol As String
        Public carbohidratos As String
        Public proteina As String
        Public preco As Double
    End Class
End Namespace
namespace Macoratti
{
    public class Pizza
    {
        public string nome;
        public string ingredientes;
        public string gordura;
        public string colesterol;
        public string carbohidratos;
        public string proteina;
        public double preco;
    }
}
VB .NET
C#

Definimos a classe Pizza com 5 campos e 5 propriedades.

Em seguida vamos criar o seguinte código:

Imports VBNET_TiposAnonimos.Macoratti
Module Module1

    Sub Main()

        Dim pizzaLista As New List(Of Pizza)() From { _
                                New Pizza() With { _
                                  .nome = "Mussarela", _
                                  .ingredientes = " mussarela , farinha, leite, ovos, tomate", _
                                  .gordura = "20g", _
                                  .colesterol = "50mg", _
                                  .carbohidratos = "35g", _
                                  .proteina = "4g", _
                                  .preco = 28.50 _
                                }, _
                                New Pizza() With { _
                                  .nome = "Calabreza", _
                                  .ingredientes = "farinha, leite, ovos, tomate, calabreza", _
                                  .gordura = "16g", _
                                  .colesterol = "65mg", _
                                  .carbohidratos = "26g", _
                                  .proteina = "4g", _
                                  .preco = 31.80 _
                                }, _
                                New Pizza() With { _
                                  .nome = "Champignon", _
                                  .ingredientes = "farinha,leite, ovos, tomate, champignon", _
                                  .gordura = "13g", _
                                  .colesterol = "58mg", _
                                  .carbohidratos = "24g", _
                                  .proteina = "6g", _
                                  .preco = 47.5 _
                                } _
                               }

        Dim PizzaMaisBarata = From pizza In pizzaLista
                                           Where pizza.preco < 10
                                          Select New Pizza() With { _
                                                      .nome = pizza.nome, _
                                                      .preco = pizza.preco _
                                                  }

        Console.WriteLine("Pizzas com preco inferior a 30 reais :")

        For Each pizza In PizzaMaisBarata
            Console.WriteLine("{0} é {1}", pizza.nome, pizza.preco)
        Next
        Console.ReadKey()
    End Sub
End Module
Using System;
using System.Collections.Generic;
using System.Linq;

namespace Macoratti
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Pizza> pizzaLista = new List<Pizza>
            {
                new Pizza
                {
                    nome="Mussarela",
                    ingredientes=" mussarela , farinha, leite, ovos, tomate",
                    gordura="20g",
                    colesterol="50mg",
                    carbohidratos="35g",
                    proteina="4g",
                    preco=28.50
                },
                new Pizza
                {
                    nome="Calabreza",
                    ingredientes="farinha, leite, ovos, tomate, calabreza",
                    gordura="16g",
                    colesterol="65mg",
                    carbohidratos="26g",
                    proteina="4g", 
                    preco=31.80
                },
                new Pizza
                {
                    nome="Champignon",
                    ingredientes="farinha,leite, ovos, tomate, champignon",
                    gordura="13g",
                    colesterol="58mg",
                    carbohidratos="24g", 
                    proteina="6g",
                    preco=47.5
                }
            };

            var PizzaMaisBarata = from pizza in pizzaLista
                                  where pizza.preco < 35
                                  select new
                                  {
                                      Nome = pizza.nome,
                                      Preco = pizza.preco
                                  };

            Console.WriteLine("Pizzas com preco inferior a 35 reais :");
            foreach (var pizza in PizzaMaisBarata)
            {
                Console.WriteLine("{0} é {1}", pizza.Preco, pizza.Nome);
            }
            Console.ReadKey();
        }
    }
}
VB .NET C#

O código acima cria uma lista com 3 Pizzas a partir da qual eu quero listar quais as pizzas tem preço inferior a 35 reais.

Feito isso agora eu posso usar a projeção ou capacidades de transformação do LINQ e criar uma estrutura e dar uma forma personalizada para o objeto Pizza original.

Eu não tenho que definir explicitamente uma nova classe para nova estrutura customizada que desejo criar. Em vez disso, posso usar o recurso dos tipos anônimos para implicitamente definir um novo tipo, com apenas duas propriedades para representar meu de dados em uma forma customizada.

O tipo anônimo é criado adicionando a palavra-chave new :

var tipoAnomimo = new { Nome="Macoratti" , site="macoratti.net", email = "macoratti@yahoo.com"};

Neste caso a variável tipoAnonimo é um tipo anônimo que possui três propriedades: nome do tipo String, site do tipo String e email do tipo String.

O tipo das propriedades é inferido pelo compilador e eu não preciso definir explicitamente os seus tipos.

No nosso exemplo usamos a new após a cláusula Select do LINQ como vemos abaixo:

   Dim PizzaMaisBarata = From pizza In pizzaLista
                                     Where pizza.preco < 10
                                     Select New Pizza() With { _
                                             .nome = pizza.nome, _
                                              .preco = pizza.preco _
                                    }

  var PizzaMaisBarata = from pizza in pizzaLista
                                  where pizza.preco < 35
                                  select new
                                  {
                                      Nome = pizza.nome,
                                      Preco = pizza.preco
                                   };	
VB .NET C#

Neste código eu estou declarando um tipo anônimo na cláusula da consulta LINQ que possui apenas duas propriedades : Nome e Preco.

A seguir estou referenciando a coleção IEnumerable deste tipo anônimo retornado pela consulta e executando uma iteração sobre eles para extrair os detalhes de nome e preço.

For Each pizza In PizzaMaisBarata
            Console.WriteLine("{0} é {1}", pizza.nome, pizza.preco)
        Next
 foreach (var pizza in PizzaMaisBarata)
  {
           Console.WriteLine("{0} é {1}", pizza.Preco, pizza.Nome);
  }

Executando o projeto iremos obter o seguinte resultado:

Dessa forma realizar consultas usando o LINQ nos da uma grande flexibilidade visto que geralmente temos que retornar um tipo com o conjunto de propriedades diferentes dos tipos já existentes

Algumas características dos tipos anônimos:

A principal limitação dos tipos anônimos é que eles não são símbolos C# válidos, sua validade existe a nível de CLR(Common Language Run-time).

Isto trás as seguintes implicações:

Eu sei é apenas LINQ mas eu gosto...

"Graças e Paz da parte de Deus Pai e da de nosso Senhor Jesus Cristo. O qual se deu a si mesmo por nossos pecados para nos livrar do presente século mau, segundo a vontade de Deus nosso Pai, ao qual glória para todo o sempre, Amém."  Gálatas 3-5

Referências:


José Carlos Macoratti