ti-enxame.com

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

No meu aplicativo Web ASP.net MVC4, uso IEnumerables, tentando seguir o mantra para programar para a interface, não para a 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.

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

27
Rowan Freeman

Há momentos em que executar uma ToList() em suas consultas linq pode ser importante 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 IEnumerable sempre que precisar de iteração, use IList quando precisar indexar diretamente e precisar de uma matriz de tamanho dinâmico (se precisar indexar em uma matriz de tamanho fixo, basta usar um matriz padrão).

Quanto ao tempo de execução, você sempre pode usar uma lista como uma variável IEnumerable, portanto, sinta-se à vontade para retornar um IEnumerable fazendo uma .ToList(); ou passando uma parâmetro como um IEnumerable executando .ToList() no IEnumerable para forçar a execução naquele momento. Apenas tome cuidado para que, sempre que forçar a execução com .ToList(), não se apegue à variável IEnumerable na qual você fez isso e a execute novamente, ou então acabará dobrando 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 si mesma. para forçar a execução e você ficará bem.

22
Jimmy Hoffa

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 Suggests nada impede que você retorne 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.

7
Brian

Outro benefício de enumerar IEnumerable cedo é que as 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.

1
Sam