LINQ - Usando ToDictionary() e ToList() - I


Hoje veremos a utilização dos métodos de extensão ToDictionary() e ToList() da LINQ.

Existem dois métodos de extensão LINQ simples que podem simplificar a tarefa de converter consultas de coleção em coleções. São os métodos ToDictionary() e ToList().

Se você é desenvolvedor .NET com certeza já conhece os recursos e vantagens da LINQ como linguagem de consulta para obter e filtrar dados de várias fontes de dados. Os operadores de consulta padrão são os métodos que formam o padrão da Language-Integrated Query (LINQ).

A maioria destes métodos operam em seqüências, onde uma seqüência é um objeto cujo tipo implementa a interface IEnumerable<T> ou a interface IQueryable<T>.

Neste artigo vamos focar nos métodos de extensão ToDictionary() e ToList() mas antes vamos recordar um conceito importante que é a deferred execution ou execução adiada.

Para saber mais sobre deferred execution leia o meu artigo:  LINQ - Entendendo Deferred Execution

LINQ - Deferred Execution e ToList()

Vamos iniciar criando um pequeno exemplo em um projeto Console onde vamos criar a classe Produto:

public class Produto
{
   public int Id { get; set; }
   public string Nome { get; set; }
   public string Categoria { get; set; }
}

Criamos uma classe POCO anêmica que vamos usar no exemplo.

A seguir vamos criar uma coleção de objetos Produto para termos uma fonte de dados com que trabalhar criando o método estático GetProdutos() na classe Produto:   

        public static List<Produto> GetProdutos()
        {
            var produtos = new List<Produto>
            {
                new Produto { Nome = "CD Player", Id = 1, Categoria = "Eletrônicos" },
                new Produto { Nome = "DVD Player", Id = 2, Categoria = "Eletrônicos" },
                new Produto { Nome = "Blu-Ray Player", Id = 3, Categoria = "Eletrônicos" },
                new Produto { Nome = "TV LCD", Id = 4, Categoria = "Eletrônicos" },
                new Produto { Nome = "Pneu 175 aro 14", Id = 5, Categoria = "Automotivos" },
                new Produto { Nome = "TV LED", Id = 6, Categoria = "Eletrônicos" },
                new Produto { Nome = "Forno Microondas", Id = 7, Categoria = "Eletrodomésticos" },
                new Produto { Nome = "Volante em couro", Id = 8, Categoria = "Automotivos" },
                new Produto { Nome = "TV Plasma", Id = 9, Categoria = "Eletrônicos" },
                new Produto { Nome = "Geladeira 440 L", Id = 10, Categoria = "Eletrodomésticos" },
                new Produto { Nome = "Forno 4 bocas", Id = 11, Categoria = "Eletrodomésticos" },
                new Produto { Nome = "Secadora", Id = 12, Categoria = "Eletrodomésticos" },
            };
            return produtos;
        }

Então, digamos que você tenha uma coleção desses objetos Produto e precise consultá-los.

Por exemplo, poderíamos obter uma enumeração de todas as instâncias de Produto em que a Categoria é igual à “Eletrônicos”:

Isso pode ser feito usando a seguinte consulta : Produto.GetProdutos().Where(p => p.Categoria == "Electrônicos");

expressa no código a seguir:

static void Main(string[] args)
{
     var produtosEletronicos = Produto.GetProdutos().Where(p => p.Categoria == "Electrônicos");
     foreach(var produto in produtosEletronicos)
     {
             Console.WriteLine(produto.Nome);
      }
      Console.ReadLine();
}

Vamos colocar um breakpoint no foreach e executar o código e a seguir vamos examinar o conteúdo de produtosEletronicos:

Observe que produtosEletronicos é um IEnumerable<Produto> e não um List<Produto>.

O que esta acontecendo aqui é o que chamamos de deferred execution ou execução adiada.

O resultado de muitos dos métodos de extensão como Where() é criar um iterador que executa a consulta conforme você a move pela lista.

Portanto, neste ponto produtosEletronicos não é um List<Produto>, mas simplesmente um IEnumerable<Produto> que será avaliado em tempo real conforme você avança pela lista.

Essa é a tal execução adiada, que é um recurso do LINQ, que adia a execução não avaliando a expressão até que você precise do resultado. Assim aqui produtosEletronicos está à espera que façamos alguma coisa com ela para que nos possa dar os resultados da lista.

Assim a exibição vai ocorrer no laço foreach quando iterarmos sobre a lista.

Podemos obter o mesmo resultado obtendo a lista de produtos da seguinte forma:

1- Usando o método AddRange

 var resultado2 = new List<Produto>();
 resultado2.AddRange(produtosEletronicos);

2- Usar o construtor de List que usa um IEnumerable<T>

var resultado3 = new List<Produto>(produtosEletronicos);
ExibeProdutos(resultado3);

Mas uma forma mais simples é usar o método de extensão ToList() da LINQ que vai pegar qualquer IEnumerable<T> e vai usá-lo para preencher um List<T>.

Isso é útil se você deseja executar uma consulta e usá-la para preencher uma lista em uma única etapa:

var produtosEletronicos = Produto.GetProdutos().Where(p => p.Categoria == "Eletrônicos").ToList();

Agora, em vez de produtosEletronicos ser um IEnumerable<T> executado dinamicamente sobre a coleção original, será uma coleção separada e as modificações na coleção original não a afetarão.

Isso tem vantagens e desvantagens.

Como regra geral, se você for apenas iterar os resultados e processá-los, não precisa (nem quer) armazená-lo em uma lista separada, pois isso apenas desperdiça memória que mais tarde precisará ser coletada como lixo.

No entanto, se você deseja salvar esse subconjunto e atribuí-lo a outra classe, usar o ToList() é muito útil para que você não precise se preocupar com alterações na coleção original.

Assim ToList() pega um IEnumerable<T> e o converte para um List<T>.

Na próxima parte do artigo vamos tratar com ToDictionary().

Pegue o projeto completo aqui : Linq_ToListToDictionary.zip

"Mas, se o nosso evangelho ainda está encoberto, é para os que se perdem que está encoberto, nos quais o deus deste século cegou o entendimento dos incrédulos, para que lhes não resplandeça a luz do evangelho da glória de Cristo, o qual é a imagem de Deus."
2 Coríntios 4:3,4

Referências:


José Carlos Macoratti