Equivalente LINQ de foreach para IEnumerable

Eu gostaria de fazer o equivalente ao seguinte no LINQ, mas não consigo descobrir como:

IEnumerable items = GetItems(); items.ForEach(i => i.DoStuff()); 

Qual é a syntax real?

Não há extensão ForEach para IEnumerable ; somente para List . Então você poderia fazer

 items.ToList().ForEach(i => i.DoStuff()); 

Como alternativa, escreva seu próprio método de extensão ForEach:

 public static void ForEach(this IEnumerable enumeration, Action action) { foreach(T item in enumeration) { action(item); } } 

Fredrik forneceu a correção, mas pode valer a pena considerar por que isso não está no framework para começar. Eu acredito que a idéia é que os operadores de consulta LINQ devem ser livres de efeitos colaterais, encheckboxndo-se com uma maneira razoavelmente funcional de olhar o mundo. Claramente ForEach é exatamente o oposto – uma construção puramente baseada em efeitos colaterais.

Isso não quer dizer que isso seja ruim – apenas pensar sobre as razões filosóficas por trás da decisão.

Atualização 7/17/2012: Aparentemente como do C # 5.0, o comportamento do foreach descrito abaixo foi alterado e ” o uso de uma variável de iteração foreach em uma expressão lambda aninhada não produz mais resultados inesperados. ” Essa resposta não se aplica a C # ≥ 5,0.

@ John Skeet e todos que preferem a palavra-chave foreach.

O problema com “foreach” em C # anterior a 5.0 , é que ele é inconsistente com a forma como o equivalente “para compreensão” funciona em outras linguagens e com como eu esperaria que funcionasse (opinião pessoal declarada aqui apenas porque outros mencionaram sua opinião sobre a legibilidade). Veja todas as questões relativas ao ” Acesso ao fechamento modificado “, bem como ” Encerramento da variável de malha considerada prejudicial “. Isso é apenas “prejudicial” por causa da maneira como “foreach” é implementado em C #.

Tome os seguintes exemplos usando o método de extensão funcionalmente equivalente àquele na resposta de @Fredrik Kalseth.

 public static class Enumerables { public static void ForEach(this IEnumerable @this, Action action) { foreach (T item in @this) { action(item); } } } 

Desculpas pelo exemplo excessivamente inventado. Estou usando apenas o Observable porque não é totalmente improvável fazer algo assim. Obviamente, existem maneiras melhores de criar este observável, estou apenas tentando demonstrar um ponto. Normalmente, o código assinado para o observável é executado de forma assíncrona e potencialmente em outro thread. 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:

 [Test] public void ForEachExtensionWin() { //Yes, I know there is an Observable.Range. var values = Enumerable.Range(0, 10); var observable = Observable.Create>(source => { values.ForEach(value => source.OnNext(() => value)); source.OnCompleted(); return () => { }; }); //Simulate subscribing and evaluating Funcs var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList(); //Win Assert.That(evaluatedObservable, Is.EquivalentTo(values.ToList())); } 

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>

 [Test] public void ForEachKeywordFail() { //Yes, I know there is an Observable.Range. var values = Enumerable.Range(0, 10); var observable = Observable.Create>(source => { foreach (var value in values) { //If you have resharper, notice the warning source.OnNext(() => value); } source.OnCompleted(); return () => { }; }); //Simulate subscribing and evaluating Funcs var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList(); //Fail Assert.That(evaluatedObservable, Is.EquivalentTo(values.ToList())); } 

Você pode usar a extensão FirstOrDefault() , que está disponível para IEnumerable . Ao retornar false do predicado, ele será executado para cada elemento, mas não se importará se ele não encontrar uma correspondência. Isso evitará a sobrecarga de ToList() .

 IEnumerable items = GetItems(); items.FirstOrDefault(i => { i.DoStuff(); return false; }); 

Eu peguei o método de Fredrik e modifiquei o tipo de retorno.

Desta forma, o método suporta execução adiada como outros methods LINQ.

EDIT: Se isso não estava claro, qualquer uso desse método deve terminar com ToList () ou qualquer outra maneira de forçar o método para trabalhar no enumerável completo. Caso contrário, a ação não seria executada!

 public static IEnumerable ForEach(this IEnumerable enumeration, Action action) { foreach (T item in enumeration) { action(item); yield return item; } } 

E aqui está o teste para ajudar a ver:

 [Test] public void TestDefferedExecutionOfIEnumerableForEach() { IEnumerable enumerable = new[] {'a', 'b', 'c'}; var sb = new StringBuilder(); enumerable .ForEach(c => sb.Append("1")) .ForEach(c => sb.Append("2")) .ToList(); Assert.That(sb.ToString(), Is.EqualTo("121212")); } 

Se você remover o ToList () no final, verá o teste falhando, já que o StringBuilder contém uma string vazia. Isso ocorre porque nenhum método forçou o ForEach a enumerar.

Mantenha seus efeitos colaterais fora do meu IEnumerable

Eu gostaria de fazer o equivalente ao seguinte no LINQ, mas não consigo descobrir como:

Como outros indicaram aqui e no exterior, espera-se que os methods LINQ e IEnumerable sejam livres de efeitos colaterais.

Você realmente quer “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.

 foreach (var i in items) i.DoStuff(); 

Eu aposto que você não quer um efeito colateral

No entanto, na minha experiência, os efeitos colaterais geralmente não são necessários. Na maioria das vezes, há uma consulta LINQ simples esperando para ser descoberta, acompanhada por uma resposta do StackOverflow.com por Jon Skeet, Eric Lippert ou Marc Gravell explicando como fazer o que você quer!

Alguns exemplos

Se você está apenas agregando (acumulando) algum valor, então deve considerar o método de extensão Aggregate .

 items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x)); 

Talvez você queira criar um novo IEnumerable partir dos valores existentes.

 items.Select(x => Transform(x)); 

Ou talvez você queira criar uma tabela de consulta:

 items.ToLookup(x, x => GetTheKey(x)) 

A lista (trocadilho não intencional) das possibilidades continua indefinidamente.

Há um lançamento experimental da Microsoft de extensões interativas para LINQ (também no NuGet , veja o perfil do RxTeams para mais links). O vídeo do Canal 9 explica isso bem.

Seus documentos são fornecidos apenas no formato XML. Eu executei esta documentação no Sandcastle para permitir que ela ficasse em um formato mais legível. Descompacte o arquivo docs e procure por index.html .

Entre muitas outras guloseimas, fornece a implementação esperada do ForEach. Ele permite que você escreva código como este:

 int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 }; numbers.ForEach(x => Console.WriteLine(x*x)); 

Se você quiser atuar como os rolos de enumeração, deve render cada item.

 public static class EnumerableExtensions { public static IEnumerable ForEach(this IEnumerable enumeration, Action action) { foreach (var item in enumeration) { action(item); yield return item; } } } 

De acordo com o PLINQ (disponível desde .Net 4.0), você pode fazer uma

 IEnumerable.AsParallel().ForAll() 

para fazer um loop foreach paralelo em um IEnumerable.

O objective do ForEach é causar efeitos colaterais. IEnumerable é para enumeração lenta de um conjunto.

Essa diferença conceitual é bem 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 nele. 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 origens e condições. Á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, sobrescrevendo a avaliação preguiçosa.

Como várias respostas já apontam, você pode adicionar esse método de extensão com facilidade. No entanto, se você não quiser fazer isso, embora eu não esteja ciente de nada parecido com isso na BCL, ainda há uma opção no namespace System , se você já tiver uma referência à Extensão reativa (e se você não usa) t, você deveria ter):

 using System.Reactive.Linq; items.ToObservable().Subscribe(i => i.DoStuff()); 

Embora os nomes dos methods sejam um pouco diferentes, o resultado final é exatamente o que você está procurando.

Muitas pessoas mencionaram isso, mas eu tive que escrevê-lo. Isso não é mais claro / legível?

 IEnumerable items = GetItems(); foreach (var item in items) item.DoStuff(); 

Curto e simples (st).

Agora temos a opção de …

  ParallelOptions parallelOptions = new ParallelOptions(); parallelOptions.MaxDegreeOfParallelism = 4; #if DEBUG parallelOptions.MaxDegreeOfParallelism = 1; #endif Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID)); 

Claro, isso abre uma nova lata de threadworms.

ps (Desculpe as fonts, é o que o sistema decidiu)

Inspirado por Jon Skeet, eu estendi sua solução com o seguinte:

Método de Extensão:

 public static void Execute(this IEnumerable source, Action applyBehavior, Func keySelector) { foreach (var item in source) { var target = keySelector(item); applyBehavior(target); } } 

Cliente:

 var jobs = new List() { new Job { Id = "XAML Developer" }, new Job { Id = "Assassin" }, new Job { Id = "Narco Trafficker" } }; jobs.Execute(ApplyFilter, j => j.Id); 

. . .

  public void ApplyFilter(string filterId) { Debug.WriteLine(filterId); } 

Eu discordo de forma respeitosa com a noção de que os methods de extensão de links devem ser livres de efeitos colaterais (não apenas porque não são, qualquer delegado pode ter efeitos colaterais).

Considere o seguinte:

  public class Element {} public Enum ProcessType { This = 0, That = 1, SomethingElse = 2 } public class Class1 { private Dictionary> actions = new Dictionary>(); public Class1() { actions.Add( ProcessType.This, DoThis ); actions.Add( ProcessType.That, DoThat ); actions.Add( ProcessType.SomethingElse, DoSomethingElse ); } // Element actions: // This example defines 3 distict actions // that can be applied to individual elements, // But for the sake of the argument, make // no assumption about how many distict // actions there may, and that there could // possibly be many more. public void DoThis( Element element ) { // Do something to element } public void DoThat( Element element ) { // Do something to element } public void DoSomethingElse( Element element ) { // Do something to element } public void Apply( ProcessType processType, IEnumerable elements ) { Action action = null; if( ! actions.TryGetValue( processType, out action ) ) throw new ArgumentException("processType"); foreach( element in elements ) action(element); } } 

O que o exemplo mostra é realmente apenas um tipo de binding 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 em seu método correspondente.

Esta abstração “abordagem funcional” vaza grande momento. Nada no nível da linguagem evita efeitos colaterais. Contanto que você consiga chamar seu lambda / delegate 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 – sobrescrever)

isso é um hack e não deve ser usado em nenhum código de produção.

 var b = srcDictionary.Select( x=> { destDictionary[x.Key] = x.Value; return true; } ).Count(); 

Para VB.NET você deve usar:

 listVariable.ForEach(Sub(i) i.Property = "Value") 

ForEach também pode ser acorrentado , basta colocar de volta para o pileline após a ação. permanecer fluente


 Employees.ForEach(e=>e.Act_A) .ForEach(e=>e.Act_B) .ForEach(e=>e.Act_C); Orders //just for demo .ForEach(o=> o.EmailBuyer() ) .ForEach(o=> o.ProcessBilling() ) .ForEach(o=> o.ProcessShipping()); //conditional Employees .ForEach(e=> { if(e.Salary<1000) e.Raise(0.10);}) .ForEach(e=> { if(e.Age >70 ) e.Retire();}); 

 public static IEnumerable ForEach(this IEnumerable enu, Action action) { foreach (T item in enu) action(item); return enu; // make action Chainable/Fluent } 

Edit2 acima código está funcionando, mas uma versão melhor está usando isso .

A edição abaixo foi um exemplo errado, apontado por Taemyr. Muito obrigado.

 Employees.ForEach(e=>e.Salary = e.Salary * 2) .Where (e=> e.Salary > 10000) .Average(e=> e.Salary); 

Ainda outro exemplo ForEach

 public static IList MapToDomain(IList addresses) { var workingAddresses = new List(); addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a))); return workingAddresses; } 

Se você está fazendo isso, por exemplo, porque você precisa do índice em sua iteração, você sempre pode usar uma construção Where:

 linqObject.Where((obj, index) => { DoWork(obj, index); return true; }).ToArray(); //MUST CALL ToArray() or ToList() or something to execute the lazy query, or the loop won't actually execute 

Isso tem o benefício adicional de que a matriz original é retornada como “inalterada” (os objects referenciados pela lista são os mesmos, embora possam não ter os mesmos dados), o que geralmente é desejável em metodologias de functional programming / cadeia como LINQ.