Qual a diferença entre IEnumerable, IQueryable e List?

Pergunta :


Qual a diferença entre IEnumerable, IQueryable e List no .NET?

Quando é melhor usar uma ou outra?

Por que o ReSharper me sugere modificar o retorno dessa função, por exemplo, de List<T>:

private List<User> GetUsers(){ ... }

para IEnumerable<T>

private IEnumerable<User> GetUsers(){ ... }

?

Autor da pergunta: Andre Figueiredo

Resposta Comunidade:

A sugestão do Resharper ocorre porque costuma ser melhor você lidar com interfaces do que tipos concretos, ou analisando de outra forma usar um tipo mais genérico do que um mais específico.

O fato de gerar um List<T> não significa que você não tem um objeto que não seja um IEnumerable<T>, afinal List<T> é derivado de IEnumerable<T>. Uma lista é uma coleção que permite que seus membros sejam enumerados, ou seja, que você vá analisando elemento por elemento. Um método GetEnumerator() é usado para obter o enumerador (uma espécie de contador para varrer os elementos). E um método MoveNext() (da IEnumerator) faz você avançar para o próximo elemento.

Qualquer objeto IEnumerable<T> permite que cada elemento vá sendo processado individualmente para cada operação necessária ao invés de processar todos elementos da coleção em cada operação separada, o que até impediria certas operações de serem realizadas.

Um IEnumerable<T> costuma ser bom para processar elementos que já estão na memória. Você pode utilizá-lo em uma consulta de banco de dados, mas terá que trazer todos os resultados do banco para a memória para depois processá-lo.

Quando você usa um .ToList(), está convertendo o resultado para uma lista. Você não dá um exemplo de utilização, mas acredito pela pergunta que você recebeu um resultado que um IQueryable<T> que é específico para o LINQ. Um IQueryable<T> também é derivado de um IEnumerable<T> e admite lazy loading permitindo uma melhor otimização de uma consulta. Ou seja, apenas os elementos realmente necessários para uma determinada operação são retornados na consulta para futura análise.

A utilização do IQueryable<T> permite a construção de árvores de expressões de consulta. Costuma ser mais adequado para utilização com banco de dados (LINQ To SQL por exemplo) e outras fontes remotas, principalmente quando precisa de paginação de resultados. Estas expressões podem ser obtidas e executadas com os métodos IQueryProvider.CreateQuery() e IQueryProvider.Execute().

Um resultado IQueryable<T> pode ser convertido para um List<T> mas normalmente é convertido para um IEnumerable<T> para dar mais flexibilidade nas operações seguintes.

Veja em exemplo usando um IEnumerable<T>:

var ent = new EntFuncionarios();
IEnumerable<Funcionario> funcionario = ent.Funcionarios; 
IEnumerable<Funcionario> temp = funcionario.Where(x => x.FuncID == 2).ToList<Funcionario>();

Todos os funcionários virão do banco de dados e depois serão analisados um a um no Where.

E com IQueryable<T>:

var ent = new EntFuncionarios();
IQueryable<Funcionario> funcionario = ent.Funcionarios; 
IEnumerable<Funcionario> temp = funcionario.Where(x => x.FuncID == 2).ToList<Funcionario>();

Uma consulta SQL é criada e somente os dados necessários são trazidos para análise desta consulta.

A maneira como o filtro de dados funciona é a grande diferença. No segundo caso uma consulta é gerada e somente quando se utiliza o .ToList<Funcionario> é que o resultado desta consulta é materializado na memória. Você gera uma consulta e não um resultado final. Este resultado filtrado pode depois ser analisado de forma mais detalhada.

Esta materialização obriga a consulta ser efetivamente executada. Como o IEnumerable<Funcionario> e o IQueryable<Funcionario> são avaliados tardiamente (lazy evaluation ou deferred execution), ou seja, é avaliado sob demanda, de acordo com a necessidade, você só consegue uma lista real quando você gera essa demanda e isto é efeito com a conversão para uma lista com .ToList<Funcionario>.

Coloquei no GitHub para referência futura.

IEnumerable, pertence ao namespace System.Collections, e que expõe um enumerador, que suporta uma iteração sobre uma simples coleção não genérico (MSDN. Interface IEnumerable, Microsoft. 2014. Disponível em: http://msdn.microsoft.com/pt-br/library/System.Collections.IEnumerable(v=vs.110).aspx. Acesso em: 27.mai.2014, tradução) e
IEnumerable<T> que pertence ao namespace System.Collections.Generic que expõe o enumerador, que suporta uma iteração simples sobre uma coleção de um tipo especificado.(MSDN. Interface IEnumerable<T>, Microsoft. 2014. Disponível em: http://msdn.microsoft.com/pt-br/library/9eekhta0(v=vs.110).aspx. Acesso em: 27.mai.2014, tradução). O IEnumerable é a base para todas as coleções que podem ser enumeradas. As interfaces possui um método por padrão a ser implementado o GetEnumerator.

Por essa ilustração percebe a grande importância dessa Interface (IEnumerable e IEnumerable<T>)

Figura 1 – “When to use IEnumerable, ICollection, IList and List”
inserir a descrição da imagem aqui
Fonte: Claudio Bernasconi’s TechBlog

List representa uma lista de objetos fortemente tipados que podem ser acessados pelo índice. Fornece métodos para pesquisar, ordenar e manipular listas. Esse classe possui várias interfaces que a implementam:

  • IList e IList

  • ICollection e ICollection

  • IReadOnlyList

  • IReadOnlyCollection

  • IEnumerable

  • IEnumerable. (MSDN. Classe List<T>. Microsoft. 2014. Disponível em: http://msdn.microsoft.com/pt-br/library/6sh2ey19(v=vs.110).aspx. Acesso em 27.mai.2014, tradução).

Ou seja, dentro de List existem muitos recursos implementados de várias interfaces. Enquanto o IEnumerable tem o comportamento o List implementa tal comportamento, e também o compilador com IEnumerable adia o trabalho até a sua execução final, enquanto o List você força o compilador a gerar os dados imediatamente. No caso do ReSharper, ele faz uma otimização, e lhe dá no caso a melhor estrutura de trabalho daquele metodo, visto que o IEnumerable<User> é bem mais simples do que o List<User>.

IQueryable fornece a funcionalidade para dar valores a consultas em uma fonte de dados específica no qual o tipo de dados não for especificado. O IQueryable interface herda o IEnumerable interface para que se ele representa uma consulta, os resultados dessa consulta podem ser enumerados. (MSDN. Interface IQueryable, Microsoft. 2014. Disponível em: http://msdn.microsoft.com/pt-br/library/system.linq.iqueryable(v=vs.110).aspx. Acesso em: 27.mai.2014, tradução). Como foi dito o IEnumerable é importante para os três itens reportados na questão, é a base dos enumeradores.

Referências:

  • IEnumerable Interface

  • IEnumerable<T> Interface

  • Figura 1

  • Interface IQueryable

IQueryable é uma interface mais específica que possui um provedor de consulta, como o LINQ-To-SQL. É um objeto especial, que ao utilizar um método, ele gera uma query ou uma estrutura de consulta para uma base de dados e retorna estes resultados como uma lista ou um enumerável.

Embora pareça, ele não necessariamente gera apenas comandos SQL: dependendo do provedor de dados, pode-se gerar qualquer consulta através dele, em teoria. Por exemplo, o MongoDB possui seu próprio provedor que gera comandos num formato próprio.

IEnumerable é uma interface que implementa objetos com capacidade de enumeração (IQueryable implementa IEnumerable, inclusive), ou seja, que define um objeto capaz de relacionar outros objetos em sequência.

Não necessariamente IEnumerable é um objeto literal. Em casos em que é usado yield return (ou melhor dizendo, usa-se uma função geradora), o que se obtém é um iterador que implementa a interface. Chamadas a este iterador podem devolver um elemento da sequência ou então a sequência toda, dependendo do método utilizado.

A priori, um objeto que implementa IEnumerable não supõe necessariamente que ele possa ser modificado. Para estes casos, permitindo ordenação ou manipulação dos objetos contidos dentro da sequência, o recomendado pela linguagem é usar um objeto que implemente ICollection (que por sinal também implementa IEnumerable).

List<T> é a implementação de um objeto da interface IEnumerable.

No caso do ReSharper, a sugestão ocorre porque IEnumerable é mais genérico e justamente suporta o retorno como uma função geradora, e não um objeto definido (yield return é a melhor maneira de ilustrar isso).

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *