Pergunta

Eu tenho um Enumerable<T> e estou procurando um método que me permite executar uma ação para cada elemento, tipo como Select mas depois de efeitos colaterais. Algo como:

string[] Names = ...;
Names.each(s => Console.Writeline(s));

ou

Names.each(s => GenHTMLOutput(s));   
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)

Eu tentei Select(s=> { Console.WriteLine(s); return s; }), mas não estava imprimindo nada.

Foi útil?

Solução

Uma maneira rápida e fácil de obter isto é:

Names.ToList().ForEach(e => ...);

Outras dicas

Você está procurando o ForEach sempre esquivo que atualmente só existe na coleção genérica List. Há muitas discussões on-line sobre se Microsoft deve ou não adicionar isso como um método LINQ. Atualmente, você tem que rolar o seu próprio:

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

Enquanto o método All() fornece capacidades semelhantes, é de casos de uso é para a realização de um teste de predicado em cada item, em vez de uma ação. Claro, ele pode ser persuadido a realizar outras tarefas, mas isso pouco muda a semântica e tornaria mais difícil para os outros para interpretar seu código (ou seja, é esse uso de All() para um teste predicado ou uma ação?).

Disclaimer: Este post já não se assemelha a minha resposta original, mas sim incorpora os cerca de sete anos de experiência que eu ganhei uma vez. I fez a edição, porque esta é uma questão altamente vistos e nenhuma das respostas existentes realmente cobriu todos os ângulos. Se você quiser ver a minha resposta original, ele está disponível no histórico de revisão para este post .


A primeira coisa a compreender aqui é C operações # linq como Select(), All(), Where(), etc, têm suas raízes na programação funcional. A idéia era trazer algumas das partes mais úteis e acessíveis de programação funcional para o mundo .Net. Isto é importante, porque um princípio fundamental da programação funcional é para operações de ser livre de efeitos colaterais. É difícil subestimar isso. No entanto, no caso de ForEach() / each(), os efeitos secundários são toda a puprose da operação. Adicionando each() ou ForEach() não é apenas fora do âmbito de programação funcional dos outros operadores LINQ, mas em oposição direta a eles.

Mas eu entendo isso parece insatisfatório. Você tem um problema real que você precisa para resolver. Por que deve todo esse marfim filosofia torre ficar no caminho de algo que pode ser verdadeiramente útil?

Eric Lippert, que estava na equipe de projeto C #, no momento, pode nos ajudar aqui. Ele recomenda o uso de um loop foreach tradicional:

[foreach ()] acrescenta zero, novo poder de representação para o idioma. Fazer isso permite que você reescrever este código perfeitamente claro:

foreach(Foo foo in foos){ statement involving foo; }

para este código:

foos.ForEach(foo=>{ statement involving foo; });

Seu ponto é que, quando você olhar atentamente para as suas opções de sintaxe, você não ganha nada de novo a partir de uma extensão ForEach() contra um loop foreach tradicional. Eu discordo parcialmente. Imagine que você tenha o seguinte:

 foreach(var item in Some.Long(and => possibly)
                         .Complicated(set => ofLINQ)
                         .Expression(to => evaluate)
 {
     // now do something
 } 

Este código ofusca o que significa, porque separa a palavra-chave foreach das operações em loop. Ela também lista o comando loop antes para as operações que definem a seqüência em que o circuito opera. Ela se sente muito mais natural querer ter essas operações vêm em primeiro lugar, e depois ter o comando do loop no final da definição da consulta. Além disso, o código é apenas feio. Parece que seria muito mais agradável para ser capaz de escrever isto:

Some.Long(and => possibly)
   .Complicated(set => ofLINQ)
   .Expression(to => evaluate)
   .ForEach(item => 
{
    // now do something
});

No entanto, mesmo aqui, eu finalmente deu a volta ao ponto de vista de Eric. Percebi código como você vê acima está chamando para uma variável adicional. Se você tem um conjunto complicado de expressões LINQ como esse, você pode adicionar informações valiosas para o seu código, primeiro atribuindo o resultado da expressão LINQ para uma nova variável:

var queryForSomeThing = Some.Long(and => possibly)
                        .Complicated(set => ofLINQ)
                        .Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
    // now do something
}

Este código se sente mais natural. Ela coloca a palavra-chave foreach volta ao lado do resto do loop, e após a definição da consulta. Acima de tudo, o nome da variável pode adicionar novas informações que serão úteis para futuros programadores que tentam entender o propósito da consulta LINQ. Mais uma vez, vemos o operador ForEach() desejado realmente acrescentou nenhum novo poder expressivo para o idioma.

No entanto, estamos ainda faltam duas características de um método de extensão ForEach() hipotética:

  1. Não é combináveis. Eu não posso acrescentar mais um .Where() ou GroupBy() ou OrderBy() após uma linha de loop foreach com o resto do código, sem criar uma nova declaração.
  2. Não é preguiçoso. Essas operações acontecem imediatamente. Não faz albaixo-me para, digamos, tem um formulário onde o usuário escolhe uma operação como um campo em uma tela maior que não seja cumprida até que o usuário pressiona um botão de comando. Esta forma pode permitir que o usuário altere sua mente antes de executar o comando. Isto é perfeitamente normal ( fácil , mesmo) com uma consulta LINQ, mas não é tão simples com uma foreach.

Você pode, naturalmente, fazer o seu próprio método de extensão ForEach(). Várias outras respostas têm implementações de já este método; não é tão complicado. No entanto, eu sinto que é desnecessário. Já existe um método existente que se encaixa o que nós queremos fazer de ambos os pontos de vista semântico e operacionais. Ambas as características ausentes acima pode ser abordada pelo uso do operador Select() existente.

Select() encaixa o tipo de transformação ou de projecção descrito por ambos os exemplos acima. Tenha em mente, porém, que eu ainda evitar a criação de efeitos colaterais. A chamada para Select() deve retornar tanto novos objectos ou de projecções dos originais. Isto às vezes pode ser auxiliado através do uso de um tipo anônimo ou objeto dinâmico (se e somente se necessário). Se você precisa os resultados de persistir em, digamos, uma variável de lista original, você pode .ToList() sempre chamada e atribuí-lo de volta para sua variável original. Eu vou acrescentar aqui que eu prefiro trabalhar com variáveis ??IEnumerable<T> tanto quanto possíveis tipos ao longo de mais concreto.

myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();

Em resumo:

  1. vara Apenas com foreach maior parte do tempo.
  2. Quando foreach realmente não vai fazer (o que provavelmente não é tão frequentemente como você pensa), o uso Select()
  3. Quando você precisa usar Select(), você ainda pode geralmente evitar efeitos colaterais (programa-visível), possivelmente através da projeção de um tipo anônimo.

Infelizmente, não há built-in maneira de fazer isso na versão atual do LINQ. A equipe quadro esqueceu de adicionar um método de extensão .ForEach. Há uma boa discussão sobre isso está acontecendo agora no seguinte blog.

http://blogs.msdn.com/kirillosenkov/ Arquivo / 2009/01/31 / foreach.aspx

É bastante fácil de adicionar um porém.

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
  foreach ( var cur in enumerable ) {
    action(cur);
  }
}

Você não pode fazer isso imediatamente com o LINQ e IEnumerable - você precisa se quer implementar seu próprio método de extensão, ou lançar sua enumeração para uma matriz com LINQ e depois chamar Array.ForEach ():

Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());

Por favor, note que, devido à forma como os tipos de valor e estruturas de trabalho, se a coleção é de um tipo de valor e você modificar os elementos da coleção Desta forma, ele não terá nenhum efeito sobre os elementos da coleção original.

Porque LINQ é projetado para ser um recurso de consulta e não um recurso de atualização que você não vai encontrar uma extensão que executa métodos em IEnumerable , porque isso lhe permitiria executar um método (potencialmente com efeitos colaterais). Neste caso, você pode muito bem ficar com

foreach (string name em nomes)
Console.WriteLine (nome);

Bem, você também pode usar a palavra-chave foreach padrão, apenas formatá-lo em um oneliner:

foreach(var n in Names.Where(blahblah)) DoStuff(n);

Desculpe, pensei que esta opção merece estar aqui:)

Existe um método ForEach fora da lista. Você poderia converter o Enumerable a Lista chamando o método ToList () e, em seguida, chamar o método ForEach fora dessa.

Como alternativa, eu ouvi de pessoas que definem o seu próprio método ForEach fora de IEnumerable. Isso pode ser feito chamando essencialmente o método ForEach, mas em vez de envolvê-lo em um método de extensão:

public static class IEnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
    {
        List<T> list = _this.ToList();
        list.ForEach(del);
        return list;
    }
}

Usando Linq paralela:

Names.AsParallel (). ForAll (name => ...)

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