Tenho algumas dúvidas sobre como funcionam os enumeradores e o LINQ. Considere estas duas seleções simples:
List<Animal> sel = (from animal in Animals
join race in Species
on animal.SpeciesKey equals race.SpeciesKey
select animal).Distinct().ToList();
ou
IEnumerable<Animal> sel = (from animal in Animals
join race in Species
on animal.SpeciesKey equals race.SpeciesKey
select animal).Distinct();
Alterei os nomes dos meus objetos originais para que isso pareça um exemplo mais genérico. A consulta em si não é tão importante. O que eu quero perguntar é o seguinte:
foreach (Animal animal in sel) { /*do stuff*/ }
Percebi que se eu usar
IEnumerable
, quando depurar e inspecionar "sel", que nesse caso é o IEnumerable, ele possui alguns membros interessantes: "inner", "outer", "innerKeySelector" e "outerKeySelector", esses dois últimos aparecem para ser delegados. O membro "interno" não possui instâncias "Animal", mas instâncias "Espécies", o que foi muito estranho para mim. O membro "externo" contém instâncias "Animal". Presumo que os dois delegados determinem qual entra e o que sai dela?Percebi que, se eu usar "Distinto", o "interno" conterá 6 itens (isso está incorreto, pois apenas 2 são Distintos), mas o "externo" conterá os valores corretos. Novamente, provavelmente os métodos delegados determinam isso, mas isso é um pouco mais do que eu sei sobre IEnumerable.
Mais importante, qual das duas opções é a melhor em termos de desempenho?
A conversão da lista do mal via .ToList()
?
Ou talvez usando o enumerador diretamente?
Se puder, explique um pouco ou ative alguns links que explicam esse uso do IEnumerable.
fonte
IEnumerable<T>
será LINQ-To-Objects após a primeira parte, o que significa que todas as manchas devem ser retornadas para executar o Feline. Por outro lado, umIQuertable<T>
permitirá que a consulta seja refinada, puxando apenas felinos malhados.Há um artigo muito bom escrito por: TechBlog de Claudio Bernasconi aqui: Quando usar IEnumerable, ICollection, IList e List
Aqui estão alguns pontos básicos sobre cenários e funções:
fonte
List
é uma implementação daIList
e, como tal, tem uma funcionalidade extra na parte superior daqueles noIList
(por exemploSort
,Find
,InsertRange
). Se você se obriga a usarIList
maisList
, você perde esses métodos que você pode exigirIReadOnlyCollection<T>
[]
aqui também uma matriz simples .Uma classe que implementa
IEnumerable
permite que você use aforeach
sintaxe.Basicamente, ele possui um método para obter o próximo item da coleção. Ele não precisa que toda a coleção esteja na memória e não sabe quantos itens estão nela,
foreach
apenas continua recebendo o próximo item até que ele se esgote.Isso pode ser muito útil em determinadas circunstâncias, por exemplo, em uma tabela massiva de banco de dados, você não deseja copiar tudo na memória antes de começar a processar as linhas.
Agora
List
implementaIEnumerable
, mas representa a coleção inteira na memória. Se você possui umIEnumerable
e você chama,.ToList()
cria uma nova lista com o conteúdo da enumeração na memória.Sua expressão linq retorna uma enumeração e, por padrão, a expressão é executada quando você itera usando o
foreach
. UmaIEnumerable
instrução linq é executada quando você itera oforeach
, mas você pode forçá-lo a iterar mais cedo usando.ToList()
.Aqui está o que eu quero dizer:
fonte
foreach
enquanto a Lista passa por, digamos, índice. Agora, se eu gostaria de saber a contagem / duração dething
antemão, o IEnumerable não ajudará, certo?List
eArray
implementarIEnumerable
. Você pode pensarIEnumerable
no menor denominador comum que funciona tanto em coleções de memória quanto em grandes que recebem um item de cada vez. Quando você liga,IEnumerable.Count()
você pode ligar para uma.Length
propriedade rápida ou passar por toda a coleção - o ponto é queIEnumerable
você não sabe. Isso pode ser um problema, mas se você está fazendoforeach
isso, não se importa - seu código funcionará com umArray
ouDataReader
o mesmo.Ninguém mencionou uma diferença crucial, ironicamente respondida em uma pergunta encerrada como duplicada.
Consulte Diferença prática entre List e IEnumerable
fonte
O mais importante a ser observado é que, usando o Linq, a consulta não é avaliada imediatamente. Ele é executado apenas como parte da iteração, resultando
IEnumerable<T>
em umforeach
- é o que todos os delegados estranhos estão fazendo.Portanto, o primeiro exemplo avalia a consulta imediatamente chamando
ToList
e colocando os resultados da consulta em uma lista.O segundo exemplo retorna um
IEnumerable<T>
que contém todas as informações necessárias para executar a consulta posteriormente.Em termos de desempenho, a resposta é que depende . Se você precisar que os resultados sejam avaliados de uma só vez (por exemplo, você está alterando as estruturas que está consultando mais tarde ou se não quiser que a iteração
IEnumerable<T>
demore muito tempo) use uma lista. Caso contrário, use umIEnumerable<T>
. O padrão deve ser usar a avaliação sob demanda no segundo exemplo, pois geralmente usa menos memória, a menos que haja um motivo específico para armazenar os resultados em uma lista.fonte
Join
isso que faz o trabalho - interno e externo são os dois lados da junção. Geralmente, não se preocupe com o que realmente está no arquivoIEnumerables
, pois será completamente diferente do seu código real. Apenas se preocupar com a saída real quando você iterar sobre ele :)A vantagem do IEnumerable é a execução adiada (geralmente com bancos de dados). A consulta não será executada até que você efetue um loop através dos dados. É uma consulta aguardando até que seja necessária (também conhecida como carregamento lento).
Se você chamar ToList, a consulta será executada ou "materializada", como eu gostaria de dizer.
Há prós e contras para ambos. Se você chamar ToList, poderá remover algum mistério sobre quando a consulta é executada. Se você seguir o IEnumerable, terá a vantagem de que o programa não realiza nenhum trabalho até que seja realmente necessário.
fonte
Vou compartilhar um conceito mal usado no qual caí em um dia:
Resultado esperado
Resultado atual
Explicação
Conforme outras respostas, a avaliação do resultado foi adiada até a chamada
ToList
ou métodos de chamada semelhantes, por exemploToArray
.Então, eu posso reescrever o código neste caso como:
Jogar arround
https://repl.it/E8Ki/0
fonte
IEnumerable
funciona, mas agora não é mais uma vez eu sei como;)Se tudo o que você deseja fazer é enumerá-los, use o
IEnumerable
.Cuidado, porém, que alterar a coleção original que está sendo enumerada é uma operação perigosa - nesse caso, você desejará
ToList
primeiro. Isso criará um novo elemento de lista para cada elemento na memória, enumerandoIEnumerable
e, portanto, terá menos desempenho se você enumerar apenas uma vez - mas mais seguro e, às vezes, osList
métodos forem úteis (por exemplo, acesso aleatório).fonte
IEnumerable
, a criação de uma lista primeiro (e a iteração sobre isso) significa que você iterará os elementos duas vezes . Portanto, a menos que você queira executar operações mais eficientes na lista, isso realmente significa desempenho inferior..ToList()
ajuda aqui;)Além de todas as respostas postadas acima, aqui estão meus dois centavos. Existem muitos outros tipos além de List que implementam IEnumerable como ICollection, ArrayList etc. Portanto, se tivermos IEnumerable como parâmetro de qualquer método, podemos passar qualquer tipo de coleção para a função. Ou seja, podemos ter um método para operar em abstração e não em nenhuma implementação específica.
fonte
Existem muitos casos (como uma lista infinita ou uma lista muito grande) em que IEnumerable não pode ser transformado em uma lista. Os exemplos mais óbvios são todos os números primos, todos os usuários do facebook com seus detalhes ou todos os itens no ebay.
A diferença é que os objetos "Lista" são armazenados "aqui e agora", enquanto os objetos "IEnumerable" funcionam "apenas um de cada vez". Portanto, se eu estiver analisando todos os itens no ebay, um de cada vez seria algo que até um computador pequeno pode suportar, mas ".ToList ()" certamente me esgotaria a memória, não importando o tamanho do meu computador. Nenhum computador pode, por si só, conter e manipular uma quantidade tão grande de dados.
[Edit] - Escusado será dizer - não é "isso ou aquilo". geralmente faria sentido usar uma lista e um IEnumerable na mesma classe. Nenhum computador no mundo poderia listar todos os números primos, porque, por definição, isso exigiria uma quantidade infinita de memória. Mas você poderia facilmente pensar em um
class PrimeContainer
que contém umIEnumerable<long> primes
, que por razões óbvias também contém umSortedList<long> _primes
. todos os números primos calculados até agora. o próximo primo a ser verificado seria executado apenas contra os primos existentes (até a raiz quadrada). Dessa forma, você obtém os dois primos um de cada vez (IEnumerable) e uma boa lista de "primos até agora", que é uma boa aproximação de toda a lista (infinita).fonte