Pergunta

Eu gostaria de fazer o equivalente das seguintes opções no LINQ, mas eu não consigo descobrir como fazer isso:

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

O que é a verdadeira sintaxe?

Foi útil?

Solução

Não há extensão ForEach para IEnumerable; apenas para List<T>. 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<T>(this IEnumerable<T> enumeration, Action<T> action)
{
    foreach(T item in enumeration)
    {
        action(item);
    }
}

Outras dicas

Fredrik forneceu a correção, mas pode valer a pena considerar por que isso não está no quadro para começar. Eu acredito que a idéia é que os operadores de consulta LINQ deve ser lateral-free-efeito, cabendo em uma maneira razoavelmente funcional de olhar o mundo. Claramente ForEach é exatamente o oposto -. A puramente construção baseada em side-effect

Isso não quer dizer que isso é uma coisa ruim para fazer -. Pensar apenas sobre as razões filosóficas por trás da decisão

Atualizar 2012/07/17: Aparentemente a partir de C # 5.0, o comportamento de foreach descrito abaixo foi alterado e " o uso de uma variável foreach iteração em uma expressão lambda aninhada já não produz resultados inesperados. " Esta resposta não faz aplicam-se a C # = 5,0.

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

O problema com "foreach" em C # antes de 5.0 , é que ele é inconsistente com a forma como o equivalente 'para a compreensão' funciona em outros idiomas, e com a forma como eu esperaria que o trabalho (opinião pessoal afirmado aqui apenas porque os outros ter mencionado a sua opinião sobre a legibilidade). Veja todas as perguntas a respeito de " Acesso ao fechamento modificado" bem como " Fechando sobre a variável do laço considerado prejudicial". Esta é apenas "prejudicial" por causa da maneira "foreach" é implementado em C #.

Siga os seguintes exemplos usando o método de extensão funcionalmente equivalente a isso em resposta de @Fredrik Kalseth.

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

Desculpas para o exemplo excessivamente artificial. Eu só estou usando Observable porque não é inteiramente muito buscado para fazer algo assim. Obviamente há melhores maneiras de criar este observável, eu só estou tentando demonstrar um ponto. Normalmente o código inscrito para o observável é executado de forma assíncrona e, potencialmente, em outro segmento. Se usar "foreach", isso pode produzir resultados muito estranhos e potencialmente não-deterministas.

O teste a seguir utilizando 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<Func<int>>(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()));
}

A seguir 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<Func<int>>(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<T>. Ao retornar false do predicado, ele será executado para cada elemento, mas não vai se importar que ele realmente não encontrar uma correspondência. Isso irá evitar a sobrecarga ToList().

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

Eu levei o método de Fredrik e modificou o tipo de retorno.

Desta forma, os suportes método execução diferida como outros métodos LINQ.

EDIT: Se isto não era claro, qualquer uso deste método deve terminar com ToList () ou qualquer outra maneira de forçar o método de trabalho no total enumeráveis. Caso contrário, a ação não seria realizada!

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

E aqui está o teste para ajudar a vê-lo:

[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
    IEnumerable<char> 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, você vai ver o teste falhar desde o StringBuilder contém uma cadeia vazia. Isso ocorre porque nenhum método forçou o ForEach para enumerar.

Mantenha o seu efeitos colaterais fora do meu IEnumerable

Eu gostaria de fazer o equivalente das seguintes opções no LINQ, mas eu não consigo descobrir como fazer isso:

Como outros apontaram aqui e no exterior LINQ e os métodos IEnumerable são esperados para ser livre de efeitos colaterais.

Você realmente quer "fazer algo" para cada item na IEnumerable? Então foreach é a melhor escolha. As pessoas não se surpreendem quando efeitos colaterais acontecer aqui.

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

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

No entanto, em minha experiência, os efeitos colaterais são geralmente não é necessária. Mais frequentemente do que não existe uma consulta LINQ simples esperando para ser descoberto acompanhado por uma resposta StackOverflow.com por qualquer Jon Skeet, Eric Lippert, ou Marc Gravell explicando como fazer o que quiser!

Alguns exemplos

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

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

Talvez você queira criar uma nova IEnumerable a partir dos valores existentes.

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

Ou talvez você quer criar uma tabela look-up:

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

A lista (trocadilho não inteiramente intencional) de possibilidades continua e continua.

Se você quer agir como os rolos de enumeração deverá produzir cada item.

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

Existe uma versão experimental pela Microsoft de extensões Interativo de LINQ (também em NuGet , consulte das RxTeams perfil para mais links). A Channel 9 vídeo explica-lo bem .

Seus documentos só são fornecidos no formato XML. Eu tenho que correr esse documentação em Sandcastle para permitir que ele seja em um formato mais legível. Descompacte os docs arquivar e procurar o index.html .

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

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };

numbers.ForEach(x => Console.WriteLine(x*x));

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

IEnumerable<T>.AsParallel().ForAll() 

para fazer um loop foreach paralelo em um IEnumerable.

O objetivo da ForEach é de causar efeitos colaterais. IEnumerable é para enumeração preguiçoso de um conjunto.

Esta diferença conceptual é bastante visível quando você considerá-lo.

SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));

Isto não executar até que você faça uma "contagem" ou um "ToList ()" ou algo sobre ele. Ele claramente não é o que se expressa.

Você deve usar as extensões IEnumerable para a criação de cadeias de iteração, definining conteúdo por suas respectivas fontes e condições. Árvores de expressão são poderoso e eficiente, mas você deve aprender a apreciar a sua natureza. E não apenas para a programação em torno deles para economizar alguns caracteres substituindo avaliação preguiçosa.

Muitas pessoas mencionou, mas eu tinha que escrevê-lo. Não é isto mais claro / mais legível?

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

curta e simples (st).

Como inúmeras respostas já apontam, você pode facilmente adicionar como um método de extensão si mesmo. No entanto, se você não quiser fazer isso, embora eu não estou ciente de qualquer coisa como isto no BCL, ainda há uma opção no namespace System, se você já tem uma referência a Reactive Extensão (e se você não fizer isso, você deve ter):

using System.Reactive.Linq;

items.ToObservable().Subscribe(i => i.DoStuff());

Embora os nomes dos métodos são um pouco diferentes, o resultado final é exatamente o que você está procurando.

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 lata nova de threadworms.

ps (Desculpe sobre as fontes, é o que o sistema decidido)

Essa abstração "abordagem funcional" vazamentos grande momento. Nada no nível da linguagem impede efeitos colaterais. Contanto que você pode fazê-lo chamar seu lambda / delegado para cada elemento no recipiente -. Você vai obter o comportamento "ForEach"

Aqui, por exemplo, uma maneira de fundir srcDictionary em destDictionary (se a chave já existe - substituições)

este é um hack, e não deve ser usado em qualquer código de produção.

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

Inspirado por Jon Skeet, eu estendeu a solução com o seguinte:

Método de extensão:

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

Cliente:

var jobs = new List<Job>() 
    { 
        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);
    }

ForEach também pode ser Chained , 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();});

Um Eager versão de implementação.

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

Editar: preguiçoso versão está usando retorno de rendimento, como este .

public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (var item in enu)
    {
        action(item);
        yield return item;
    }
}

A versão preguiçoso necessidades para ser materializado, ToList (), por exemplo, caso contrário, nada acontece. veja abaixo grandes comentários de ToolmakerSteve.

IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
    .ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();

I manter tanto foreach () e ForEachLazy () na minha biblioteca.

Eu respectually em desacordo com a noção de que os métodos de extensão da ligação deve ser livre (não só porque eles não são, qualquer delegado pode executar efeitos colaterais) efeito colateral.

Considere o seguinte:

   public class Element {}

   public Enum ProcessType
   {
      This = 0, That = 1, SomethingElse = 2
   }

   public class Class1
   {
      private Dictionary<ProcessType, Action<Element>> actions = 
         new Dictionary<ProcessType,Action<Element>>();

      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<Element> elements )
      {
         Action<Element> 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 ligação tardia que permite invocar um dos muitos possíveis ações que têm efeitos colaterais sobre uma sequência de elementos, sem ter que escrever uma construção grande mudança para decodificar o valor que define a ação e traduzi-lo em seu método correspondente.

Para VB.NET você deve usar:

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

Para se manter um fluente pode usar esse truque:

GetItems()
    .Select(i => new Action(i.DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();

MoreLINQ tem IEnumerable<T>.ForEach e uma tonelada de outras extensões úteis. Provavelmente não vale a pena tomar a dependência apenas para ForEach, mas há um monte de coisas úteis lá.

https://www.nuget.org/packages/morelinq/

https://github.com/morelinq/MoreLINQ

No entanto, outro Exemplo ForEach

public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
    var workingAddresses = new List<AddressEntry>();

    addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));

    return workingAddresses;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top