Dado este código:
IEnumerable<object> FilteredList()
{
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
yield return item;
}
}
Por que não devo codificá-lo dessa maneira?
IEnumerable<object> FilteredList()
{
var list = new List<object>();
foreach( object item in FullList )
{
if( IsItemInPartialList( item ) )
list.Add(item);
}
return list;
}
Eu meio que entendo o que a yield
palavra - chave faz. Diz ao compilador para criar um certo tipo de coisa (um iterador). Mas por que usá-lo? Além de ser um pouco menos de código, o que isso faz por mim?
FullList.Where(IsItemInPartialList)
:)Respostas:
Usar
yield
torna a coleção preguiçosa.Digamos que você só precise dos cinco primeiros itens. Do seu jeito, eu tenho que percorrer a lista inteira para obter os cinco primeiros itens. Com
yield
, eu apenas percorrer os cinco primeiros itens.fonte
FullList.Where(IsItemInPartialList)
será tão preguiçoso. Somente, requer muito menos código personalizado --- gunk --- gerado pelo compilador. E menos tempo para o desenvolvedor escrever e manter. (Obviamente, esse foi apenas este exemplo) #yield return
) sempre que possível.O benefício dos blocos de iteradores é que eles funcionam preguiçosamente. Então você pode escrever um método de filtragem como este:
Isso permitirá que você filtre um fluxo pelo tempo que desejar, nunca armazenando em buffer mais do que um único item por vez. Se você só precisa do primeiro valor da sequência retornada, por exemplo, por que deseja copiar tudo para uma nova lista?
Como outro exemplo, você pode criar facilmente um fluxo infinito usando blocos iteradores. Por exemplo, aqui está uma sequência de números aleatórios:
Como você armazenaria uma sequência infinita em uma lista?
Minha série de blogs Edulinq fornece uma implementação de exemplo do LINQ to Objects, que faz uso intenso de blocos de iteradores. O LINQ é fundamentalmente preguiçoso onde pode estar - e colocar as coisas em uma lista simplesmente não funciona dessa maneira.
fonte
RandomSequence
ou não do seu. Para mim, IEnumerable significa - primeiro e acima de tudo - que eu posso iterar com o foreach, mas isso obviamente levaria a um loop inifinito aqui. Eu consideraria isso um uso muito perigoso do conceito IEnumerable, mas YMMV.IEnumerable<BigInteger>
representação da sequência de Fibonacci, por exemplo. Você pode usarforeach
com ele, mas nada sobreIEnumerable<T>
garantias de que será finito.Com o código "list", você precisa processar a lista completa antes de passar para a próxima etapa. A versão "yield" passa o item processado imediatamente para a próxima etapa. Se o "próximo passo" contiver um ".Take (10)", a versão "yield" processará apenas os 10 primeiros itens e esquecerá o resto. O código da "lista" teria processado tudo.
Isso significa que você vê a maior diferença quando precisa fazer muito processamento e / ou possui longas listas de itens a serem processados.
fonte
Você pode usar
yield
para retornar itens que não estão em uma lista. Aqui está uma pequena amostra que pode percorrer infinitamente uma lista até ser cancelada.Isso escreve
... etc para o console até ser cancelado.
fonte
Quando o código acima é usado para percorrer FilteredList () e assumindo que item.Name == "James" será atendido no segundo item da lista, o método usando
yield
renderá duas vezes. Este é um comportamento preguiçoso.Onde como o método using list adicionará todos os n objetos à lista e passará a lista completa ao método de chamada.
Este é exatamente um caso de uso em que a diferença entre IEnumerable e IList pode ser destacada.
fonte
O melhor exemplo do mundo real que eu já vi para o uso de
yield
seria calcular uma sequência de Fibonacci.Considere o seguinte código:
Isso retornará:
Isso é bom porque permite calcular uma série infinita de maneira rápida e fácil, permitindo que você use as extensões do Linq e consulte apenas o que você precisa.
fonte
Às vezes é útil, às vezes não. Se todo o conjunto de dados precisar ser examinado e retornado, não haverá nenhum benefício em usar o rendimento, porque tudo o que ele fez foi introduzir uma sobrecarga.
Quando o rendimento realmente brilha é quando apenas um conjunto parcial é retornado. Eu acho que o melhor exemplo é a classificação. Suponha que você tenha uma lista de objetos contendo uma data e uma quantia em dólares deste ano e gostaria de ver os primeiros (5) registros do ano.
Para conseguir isso, a lista deve ser classificada em ordem crescente por data e, em seguida, as 5 primeiras são obtidas. Se isso foi feito sem rendimento, toda a lista teria que ser classificada, para garantir que as duas últimas datas estivessem em ordem.
No entanto, com o rendimento, uma vez estabelecidos os 5 primeiros itens, a classificação é interrompida e os resultados estão disponíveis. Isso pode economizar uma grande quantidade de tempo.
fonte
A declaração de retorno de rendimento permite retornar apenas um item de cada vez. Você está coletando todos os itens em uma lista e retornando novamente a lista, que é uma sobrecarga de memória.
fonte