Por que devo usar a Lista <T> sobre IEnumerable <T>?

24

No meu aplicativo Web ASP.net MVC4, eu uso IEnumerables, tentando seguir o mantra para programar na interface, não na implementação.

Return IEnumerable(Of Student)

vs

Return New List(Of Student)

As pessoas estão me dizendo para usar List e não IEnumerable, porque listas forçam a consulta a ser executada e IEumerable não.

Esta é realmente a melhor prática? Existe alguma alternativa? Eu me sinto estranho usando objetos concretos onde uma interface pode ser usada. Meu sentimento estranho é justificado?

Rowan Freeman
fonte
2
Primeiro, por que é bom forçar a execução da consulta? Segundo, você deve monitorar e criar um perfil das chamadas do banco de dados para avaliar se essa consideração técnica tem algum mérito.
usar o seguinte comando
2
Dizem que todas as consultas devem ser executadas para que o modelo seja feito e carregado pronto para a visualização. Ou seja, a visualização deve receber tudo e não estar consultando o banco de dados.
Rowan Freeman
3
Esta questão StackOverflow cobre muito bem.
Karl Bielefeldt
Essa é uma boa resposta, mas quero saber que é relevante para o MVC. Por que a exibição não pode receber IEnumerables para que todas as consultas sejam executadas na hora certa?
Rowan Freeman
2
"Dizem que todas as consultas devem ser executadas para que o modelo seja feito e carregado pronto para a visualização. Ou seja, a visualização deve receber tudo e não estar consultando o banco de dados". Isso não faz sentido. Se você passar um IEnumerable, sua exibição não saberá nem se importará se está consultando o banco de dados. E é assim que deve ser.
user16764

Respostas:

21

Há momentos ToList()em que as consultas linq podem ser importantes para garantir que suas consultas sejam executadas no momento e na ordem que você espera. No entanto, esses cenários são raros e nada com que se deve preocupar muito até que eles realmente os encontrem.

Para encurtar a história, use IEnumerablesempre que precisar de iteração, use IListquando precisar indexar diretamente e precisar de uma matriz de tamanho dinâmico (se precisar indexar em uma matriz de tamanho fixo, basta usar uma matriz padrão).

Como para a coisa tempo de execução, você sempre pode usar uma lista como uma IEnumerablevariável, tão à vontade para retornar um IEnumerablefazendo uma .ToList();, ou passar um parâmetro como um IEnumerableexecutando .ToList()na IEnumerableexecução de força ali mesmo. Apenas tome cuidado para que, sempre que forçar a execução .ToList(), não se apegue à IEnumerablevariável que você acabou de fazer e a execute novamente, ou então você acabará duplicando as iterações na sua consulta LINQ desnecessariamente.

No que diz respeito ao MVC, não há realmente nada de especial a ser observado aqui. Ele seguirá as mesmas regras de tempo de execução que o restante do .NET. Acho que você pode ter alguém confuso causado pela semântica de execução atrasada no passado e culpar o MVC dizendo que isso está relacionado de alguma forma. não. A semântica de execução atrasada confunde todo mundo no começo (e até um bom tempo depois; eles podem ser um pouco complicados). Mais uma vez, porém, não se preocupe até que você realmente se preocupe em garantir que uma consulta LINQ não seja executada duas vezes ou exija que ela seja executada em uma determinada ordem em relação a outro código; nesse ponto, atribua sua variável a ela mesma. ToList () para forçar a execução e você ficará bem.

Jimmy Hoffa
fonte
É ruim dar uma visão IEnumerables? Você deve fornecer Listas para que as consultas sejam executadas no momento em que a visualização as recebe?
Rowan Freeman
@RowanFreeman Acabei de adicionar uma edição para responder a isso. Eu acho que você encontrou alguém que encontrou algo que eles não entendiam completamente (não pode culpá-los, a execução atrasada é seriamente complexa e confusa) e atribuiu isso a um mau joojoo em vez de trabalhar para entender o comportamento completo da execução atrasada semântica.
Jimmy Hoffa
Boa resposta. Então, eu realmente precisarei usar .ToList ()? Até agora, meu aplicativo funciona bem usando apenas IEnumerables e passando-os do modelo para a visualização. Nenhuma lista ou .ToList (). Minha pergunta não é sobre funcionalidade - eu sei que meu aplicativo funciona. Minha pergunta é uma das melhores práticas.
Rowan Freeman
4
A melhor prática do @RowanFreeman é usar a interface mínima que ainda atende aos seus requisitos. O IEnumerable está fazendo isso agora para você, portanto não se preocupe em alterá-lo. Dito isto, chegará o dia em que você terá uma consulta executando 3 ou 10 vezes e não entenderá o porquê, ou esperará que uma consulta seja executada antes de uma inserção apenas para descobrir que é executada posteriormente, esses são os momentos que você precisa reconhecer .ToList () forçará a execução quando você desejar, e reiterar um IEnumerable a partir de uma consulta LINQ várias vezes executará a consulta inteira várias vezes; Corrigir o seu execução quando esses eventos acontecem
Jimmy Hoffa
11
Você nem deve usar - Listpassear por um Listimplica que o conteúdo da lista será modificado. Se você deseja retornar uma coleção, use IReadOnlyCollection. Listé para uso em métodos e para troca entre métodos que modificam a lista. É isso aí!
11116 ErikE
7

Existem dois problemas.

IENumerable<Data> query = MyQuery();

//Later
foreach (Data item in query) {
  //Process data
}

Quando o loop "Process Data" é atingido, a consulta pode não ser mais válida. Por exemplo, se a consulta estiver sendo executada em um DataContext que já foi Disposed, seu código emitirá uma exceção. Esse tipo de coisa se torna muito confusa quando você está processando uma consulta em um contexto diferente daquele em que a criou.

Um problema secundário é que sua conexão não será liberada até que o loop "Process Data" seja concluído. Isso é apenas um problema se "Process Data" for complexo. Isso é mencionado em http://msdn.microsoft.com/en-us/library/bb386929.aspx :

P. Por quanto tempo minha conexão com o banco de dados permanece aberta?

R. Uma conexão normalmente permanece aberta até você consumir os resultados da consulta. Se você espera levar algum tempo para processar todos os resultados e não se opõe ao armazenamento em cache, aplique ToList à consulta. Em cenários comuns em que cada objeto é processado apenas uma vez, o modelo de streaming é superior no DataReader e no LINQ to SQL.

Portanto, é por esses motivos que você está sendo incentivado a garantir que a consulta seja realmente executada, por exemplo, chamando ToList(). No entanto, como Jimmy sugere , nada o impede de retornar sua lista como um IEnumerable.

Como regra geral, eu recomendo evitar a iteração sobre um IEnumerable mais de uma vez. Supondo que os consumidores do seu código sigam essa regra, não considero uma preocupação que alguém possa acessar o banco de dados duas vezes executando a consulta duas vezes.

Brian
fonte
1

Outro benefício de enumerar o IEnumerableinício é que exceções serão lançadas no local apropriado. Isso ajuda na depuração.

Por exemplo, se você tiver uma exceção de deadlock em uma das visualizações do Razor, não será realmente tão claro como se a exceção ocorresse durante um dos métodos de acesso a dados.

Sam
fonte
Qualquer método que retorne um IEnumerableque possa ser lançado provavelmente está cometendo um erro. Os IEnumerablemétodos diferidos devem ser divididos em dois: um método não diferido verifica os parâmetros e é configurado, lançando, se necessário (por exemplo, devido a um argumento nulo). Em seguida, ele retorna uma chamada para a implementação privada que é adiada. Não acho que meus comentários estejam totalmente em desacordo com sua resposta, mas acho que você deixou de lado um aspecto importante em sua resposta, que é o significado semântico de usar IEnumerablevs. a List(mutação) vs. IReadOnlyCollection(nenhum benefício para adiamento) .
11116 ErikE