Gostaria de fazer o equivalente ao seguinte no LINQ, mas não consigo descobrir como:
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
Qual é a sintaxe real?
linq
foreach
ienumerable
tags2k
fonte
fonte
ForEach()
.ForEach
extensão .foreach (var i in items) i.Dostuff();
Respostas:
Não há extensão ForEach para
IEnumerable
; apenas paraList<T>
. Então você poderia fazerComo alternativa, escreva seu próprio método de extensão ForEach:
fonte
IENumerable<T>
no método de extensão. Como:public static IEnumerable<T> ForAll<T>(this IEnumerable<T> numeration, Action<T> action) { foreach (T item in enumeration) { action(item); } return enumeration; }
Fredrik forneceu a correção, mas pode valer a pena considerar por que isso não está na estrutura, para começar. Acredito que a idéia é que os operadores de consulta LINQ sejam livres de efeitos colaterais, adaptando-se a uma maneira razoavelmente funcional de olhar o mundo. Claramente, o ForEach é exatamente o oposto - uma construção puramente baseada em efeitos colaterais.
Isso não quer dizer que isso é uma coisa ruim a se fazer - basta pensar nas razões filosóficas por trás da decisão.
fonte
Seq.iter
não retorna nada, muito menos um novo seq com elementos afetados pela lateral. É realmente apenas sobre efeitos colaterais. Você pode estar pensandoSeq.map
, porqueSeq.iter
é precisamente equivalente a umForEach
método de extensão ativadoIEnumerabe<_>
.Atualização 7/17/2012: Aparentemente, a partir do C # 5.0, o comportamento
foreach
descrito abaixo foi alterado e " o uso de umaforeach
variável de iteração em uma expressão lambda aninhada não produz mais resultados inesperados " . Esta resposta não se aplica ao C # ≥ 5.0 .@ John Skeet e todos que preferem a palavra-chave foreach.
O problema com "foreach" em C # anterior à 5.0 é que ele é inconsistente com o modo como a "compreensão" equivalente funciona em outros idiomas e com o que eu esperaria que funcionasse (opinião pessoal declarada aqui apenas porque outros mencionaram seus opinião sobre legibilidade). Consulte todas as perguntas sobre " Acesso ao fechamento modificado " e " Variável sobre o loop considerada prejudicial ". Isso é apenas "prejudicial" devido à maneira como "foreach" é implementado em C #.
Pegue os exemplos a seguir, usando o método de extensão funcionalmente equivalente ao da resposta de @Fredrik Kalseth.
Desculpas pelo exemplo excessivamente artificial. Só estou usando o Observable porque não é totalmente buscado fazer algo assim. Obviamente, existem maneiras melhores de criar isso observável, estou apenas tentando demonstrar um ponto. Normalmente, o código inscrito no observável é executado de forma assíncrona e potencialmente em outro encadeamento. Se usar "foreach", isso pode produzir resultados muito estranhos e potencialmente não determinísticos.
O seguinte teste usando o método de extensão "ForEach" passa:
O seguinte falha com o erro:
Esperado: equivalente a <0, 1, 2, 3, 4, 5, 6, 7, 8, 9> Mas foi: <9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9>
fonte
Você pode usar a
FirstOrDefault()
extensão, que está disponível paraIEnumerable<T>
. Ao retornarfalse
do predicado, ele será executado para cada elemento, mas não se importará em não encontrar uma correspondência. Isso evitará aToList()
sobrecarga.fonte
foreach(Item i in GetItems()) { i.DoStuff();}
você tomou mais caracteres e tornou extremamente confusoitems.All(i => { i.DoStuff(); return true; }
Peguei o método de Fredrik e modifiquei o tipo de retorno.
Dessa forma, o método suporta execução adiada como outros métodos LINQ.
Edição: Se isso não ficou claro, qualquer uso desse método deve terminar com ToList () ou qualquer outra maneira de forçar o método a trabalhar no enumerável completo. Caso contrário, a ação não seria executada!
E aqui está o teste para ajudar a vê-lo:
Se você remover o ToList () no final, verá o teste falhando, pois o StringBuilder contém uma sequência vazia. Isso ocorre porque nenhum método forçou o ForEach a enumerar.
fonte
ForEach
é interessante, mas não corresponde ao comportamento deList.ForEach
, cuja assinatura épublic void ForEach(Action<T> action)
. Também não corresponde ao comportamento daObservable.ForEach
extensãoIObservable<T>
, cuja assinatura épublic static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext)
. O FWIW, oForEach
equivalente nas coleções Scala, mesmo as preguiçosas, também possui um tipo de retorno equivalente a nulo em C #.Select
comToList
. O objetivoForEach
é não precisar ligarToList
. Deve ser executado imediatamente.Mantenha seus efeitos colaterais fora do meu IEnumerable
Como outros já apontaram aqui e no exterior,
IEnumerable
espera-se que o LINQ e os métodos sejam livres de efeitos colaterais.Deseja realmente "fazer alguma coisa" para cada item no IEnumerable? Então
foreach
é a melhor escolha. As pessoas não ficam surpresas quando os efeitos colaterais acontecem aqui.Aposto que você não quer um efeito colateral
No entanto, na minha experiência, efeitos colaterais geralmente não são necessários. Na maioria das vezes, existe uma consulta LINQ simples aguardando descoberta, acompanhada por uma resposta do StackOverflow.com por Jon Skeet, Eric Lippert ou Marc Gravell explicando como fazer o que você deseja!
Alguns exemplos
Se você está apenas agregando (acumulando) algum valor, considere o
Aggregate
método de extensão.Talvez você queira criar um novo a
IEnumerable
partir dos valores existentes.Ou talvez você queira criar uma tabela de consulta:
A lista (trocadilhos não totalmente intencionados) de possibilidades continua.
fonte
Se você deseja atuar como os rolos de enumeração, deve gerar cada item.
fonte
Existe uma versão experimental da Microsoft das extensões interativas para o LINQ (também no NuGet , consulte o perfil do RxTeams para obter mais links). O vídeo do Channel 9 explica bem.
Seus documentos são fornecidos apenas no formato XML. Eu executei esta documentação no Sandcastle para permitir que ela esteja em um formato mais legível. Descompacte o arquivo morto do docs e procure index.html .
Entre muitas outras vantagens, ele fornece a implementação ForEach esperada. Permite escrever código como este:
fonte
De acordo com o PLINQ (disponível desde .Net 4.0), você pode fazer uma
para executar um loop foreach paralelo em um IEnumerable.
fonte
O objetivo do ForEach é causar efeitos colaterais. IEnumerable é para enumeração lenta de um conjunto.
Essa diferença conceitual é bastante visível quando você a considera.
SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));
Isso não será executado até que você faça uma "contagem" ou um "ToList ()" ou algo assim. Claramente não é o que é expresso.
Você deve usar as extensões IEnumerable para configurar cadeias de iteração, definindo o conteúdo por suas respectivas fontes e condições. As árvores de expressão são poderosas e eficientes, mas você deve aprender a apreciar sua natureza. E não apenas para programar em torno deles para salvar alguns caracteres substituindo a avaliação preguiçosa.
fonte
Muitas pessoas mencionaram, mas eu tive que escrever. Isso não é mais claro / mais legível?
Curto e simples (st).
fonte
GetItems()
método retorna.foreach (var item in GetItems()) item.DoStuff();
é apenas uma beleza.Agora temos a opção de ...
Obviamente, isso abre uma nova lata de threadworms.
ps (Desculpe pelas fontes, foi o que o sistema decidiu)
fonte
Como inúmeras respostas já apontam, você pode adicionar facilmente esse método de extensão. No entanto, se você não quiser fazer isso, embora eu não esteja ciente de algo assim na BCL, ainda há uma opção no
System
espaço para nome, se você já tiver uma referência à Extensão Reativa (e se não quiser). , Você devia ter):Embora os nomes dos métodos sejam um pouco diferentes, o resultado final é exatamente o que você está procurando.
fonte
O ForEach também pode ser acorrentado , basta retornar à linha de pilotos após a ação. permaneça fluente
Uma versão ansiosa da implementação.
Edit: um preguiçoso versão está usando retorno de rendimento, como este .
A versão Lazy PRECISA ser materializada, ToList () por exemplo, caso contrário, nada acontece. veja abaixo ótimos comentários de ToolmakerSteve.
Eu mantenho ForEach () e ForEachLazy () na minha biblioteca.
fonte
foreach(T item in enu) {action(item); yield return item;}
foreach (T item in enu) { action1(item); action2(item); action3(item); }
? Um único loop explícito. Eu acho que se era importante fazer todas as ações1 antes de começar a fazer ações2.ProcessShipping()
? Você envia um e-mail ao comprador, cobra o cartão de crédito, mas nunca envia as coisas para ele? Certamente pedindo problemas.Essa abstração de "abordagem funcional" vaza muito tempo. Nada no nível do idioma evita efeitos colaterais. Contanto que você possa chamar seu lambda / delegado para cada elemento no contêiner - você obterá o comportamento "ForEach".
Aqui, por exemplo, uma maneira de mesclar srcDictionary em destDictionary (se a chave já existir - substitui)
isso é um hack e não deve ser usado em nenhum código de produção.
fonte
foreach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }
? Se não estiver no código de produção, onde e por que você deveria usá-lo?Inspirado por Jon Skeet, estendi sua solução com o seguinte:
Método de extensão:
Cliente:
. . .
fonte
O MoreLinq possui várias
IEnumerable<T>.ForEach
outras extensões úteis. Provavelmente não vale a pena assumir a dependência apenas porForEach
, mas há muitas coisas úteis lá.https://www.nuget.org/packages/morelinq/
https://github.com/morelinq/MoreLINQ
fonte
Eu discordo respeitosamente da noção de que os métodos de extensão de link devem ser livres de efeitos colaterais (não apenas porque não são, qualquer delegado pode executar efeitos colaterais).
Considere o seguinte:
O que o exemplo mostra é realmente apenas um tipo de ligação tardia que permite invocar uma das muitas ações possíveis com efeitos colaterais em uma sequência de elementos, sem precisar escrever uma grande construção de chave para decodificar o valor que define a ação e traduzir no seu método correspondente.
fonte
Para permanecer fluente, pode-se usar esse truque:
fonte
Para o VB.NET, você deve usar:
fonte
List
ouIList
. Para geralIEnumerable
, escreva o equivalente em VB do método de extensão ForEach da resposta aceita .Mais um
ForEach
exemplofonte