it-swarm-pt.tech

IEnumerable vs List - O que usar? Como eles funcionam?

Eu tenho algumas dúvidas sobre como Enumeradores funcionam e LINQ. Considere estes dois selects 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();

Eu mudei 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 é isto:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. Notei que se eu usar IEnumerable, quando eu depurar e inspecionar "sel", que nesse caso é o IEnumerable, ele tem alguns membros interessantes: "inner", "outer", "innerKeySelector" e "outerKeySelector", estes últimos parecem ser delegados. O membro "interno" não tem instâncias de "Animal", mas instâncias de "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?

  2. Notei que se eu usar "Distinto", o "interno" contém 6 itens (isso é incorreto, pois apenas 2 são Distintos), mas o "externo" contém os valores corretos. Novamente, provavelmente os métodos delegados determinam isso, mas isso é um pouco mais do que eu sei sobre IEnumerable.

  3. Mais importante ainda, qual das duas opções é a melhor em termos de desempenho?

A malvada List conversion via .ToList()?

Ou talvez usando o enumerador diretamente?

Se você puder, por favor, explique um pouco ou lance alguns links que expliquem esse uso de IEnumerable.

589
Axonn

IEnumerable descreve o comportamento, enquanto List é uma implementação desse comportamento. Quando você usa IEnumerable, você dá ao compilador a chance de adiar o trabalho até mais tarde, possivelmente otimizando ao longo do caminho. Se você usar ToList (), forçará o compilador a reificar os resultados imediatamente.

Sempre que estou "empilhando" expressões LINQ, eu uso IEnumerable, porque especificando apenas o comportamento, dou ao LINQ uma chance de adiar a avaliação e possivelmente otimizar o programa. Lembre-se de como o LINQ não gera o SQL para consultar o banco de dados até enumerá-lo? Considere isto:

public IEnumerable<Animals> AllSpotted()
{
    return from a in Zoo.Animals
           where a.coat.HasSpots == true
           select a;
}

public IEnumerable<Animals> Feline(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Felidae"
           select a;
}

public IEnumerable<Animals> Canine(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Canidae"
           select a;
}

Agora você tem um método que seleciona uma amostra inicial ("AllSpotted"), além de alguns filtros. Então agora você pode fazer isso:

var Leopards = Feline(AllSpotted());
var Hyenas = Canine(AllSpotted());

Então, é mais rápido usar a Lista sobre IEnumerable? Apenas se você quiser evitar que uma consulta seja executada mais de uma vez. Mas é melhor no geral? Bem no acima, Leopardos e Hienas são convertidos em consultas SQL únicas cada , e o banco de dados retorna apenas as linhas que são relevantes. Mas, se tivéssemos retornado uma Lista de AllSpotted(), ela poderia ficar mais lenta, porque o banco de dados poderia retornar muito mais dados do que o necessário, e nós perdemos ciclos fazendo a filtragem no cliente.

Em um programa, pode ser melhor adiar a conversão de sua consulta para uma lista até o final, então, se eu for enumerar através de Leopardos e Hienas mais de uma vez, eu faria o seguinte:

List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();
662
Chris Wenham

Existe um artigo muito bom escrito por: TechBlog de Claudio Bernasconi aqui: Quando usar IEnumerable, ICollection, IList e List

Aqui alguns pontos básicos sobre cenários e funções:

enter image description hereenter image description here

129
rubStackOverflow

Uma classe que implementa IEnumerable permite que você use a sintaxe foreach.

Basicamente, ele tem um método para obter o próximo item na 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 continua recebendo o próximo item até acabar.

Isso pode ser muito útil em certas circunstâncias, por exemplo, em uma tabela de banco de dados massiva, você não quer copiar tudo na memória antes de começar a processar as linhas.

Agora List implementa IEnumerable, mas representa a coleção inteira na memória. Se você tiver um IEnumerable e chamar .ToList(), criará 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ê faz uma iteração usando o foreach. Uma instrução IEnumerable linq é executada quando você faz iteração do foreach, mas você pode forçá-lo a repetir iterativamente usando .ToList().

Aqui está o que eu quero dizer:

var things = 
    from item in BigDatabaseCall()
    where ....
    select item;

// this will iterate through the entire linq statement:
int count = things.Count();

// this will stop after iterating the first one, but will execute the linq again
bool hasAnyRecs = things.Any();

// this will execute the linq statement *again*
foreach( var thing in things ) ...

// this will copy the results to a list in memory
var list = things.ToList()

// this won't iterate through again, the list knows how many items are in it
int count2 = list.Count();

// this won't execute the linq statement - we have it copied to the list
foreach( var thing in list ) ...
120
Keith

Ninguém mencionou uma diferença crucial, ironicamente respondeu a uma pergunta fechada como uma duplicata disso.

IEnumerable é somente leitura e List não é.

Veja Diferença prática entre List e IEnumerable

74
CAD bloke

O mais importante é perceber que, usando o Linq, a consulta não é avaliada imediatamente. Ele é executado apenas como parte da iteração através do IEnumerable<T> resultante em uma foreach - é 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 mais tarde.

Em termos de desempenho, a resposta é depende . Se você precisar que os resultados sejam avaliados de uma só vez (digamos, você está alterando as estruturas que está consultando mais tarde, ou se não quiser que a iteração sobre o IEnumerable<T> demore muito tempo) use uma lista. Além disso, use um IEnumerable<T>. O padrão deve ser usar a avaliação por demanda no segundo exemplo, já que isso geralmente usa menos memória, a menos que haja um motivo específico para armazenar os resultados em uma lista.

64
thecoop

A vantagem do IEnumerable é a execução adiada (geralmente com bancos de dados). A consulta não será executada até você realmente percorrer os dados. É uma consulta aguardando até que seja necessária (também conhecido 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ê mantiver IEnumerable, terá a vantagem de que o programa não faz nenhum trabalho até que seja realmente necessário.

37
Matt Sherman

Vou compartilhar um conceito mal usado que eu caí em um dia:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));


// updating existing list
names[0] = "ford";

// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

Resultado esperado

// I was expecting    
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari

Resultado atual

// what printed actualy   
print( startingWith_M.ToList() ); // mazda
print( startingWith_F.ToList() ); // ford, fiat, ferrari

Explicação

De acordo com outras respostas, a avaliação do resultado foi adiada até chamar ToList ou métodos de invocação semelhantes, por exemplo, ToArray.

Então eu posso reescrever o código neste caso como:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

// updating existing list
names[0] = "ford";

// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

Jogar por aí

https://repl.it/E8Ki/0

16
amd

Se tudo o que você quer fazer é enumerá-los, use o IEnumerable.

Cuidado, porém, que mudar a coleção original sendo enumerada é uma operação perigosa - neste caso, você vai querer ToList primeiro. Isso criará um novo elemento de lista para cada elemento na memória, enumerando IEnumerable e, portanto, com menos desempenho se você enumerar apenas uma vez - mas mais seguro e às vezes os métodos List são úteis (por exemplo, em acesso aleatório).

15
Daren Thomas

Além de todas as respostas postadas acima, aqui estão meus dois centavos. Existem muitos outros tipos diferentes de List que implementam IEnumerable como ICollection, ArrayList etc. Então, 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, não qualquer implementação específica.

5
Ananth

Existem muitos casos (como uma lista infinita ou uma lista muito grande) onde 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 "List" são armazenados "aqui e agora", enquanto os objetos "IEnumerable" funcionam "apenas um de cada vez". Então, se eu estou passando por todos os itens no ebay, um de cada vez seria algo que até mesmo um pequeno computador pode manipular, mas ".ToList ()" certamente me deixaria sem memória, não importa o tamanho do meu computador. Nenhum computador pode conter e manipular uma quantidade tão grande de dados.

0
LongChalk