Inspirado por outra pergunta sobre a Zip
função ausente :
Por que não há ForEach
método de extensão na Enumerable
classe? Ou em qualquer lugar? A única classe que obtém um ForEach
método é List<>
. Existe uma razão pela qual está faltando (desempenho)?
c#
.net
vb.net
extension-methods
Cameron MacFarland
fonte
fonte
Parallel.ForEach
paraEnumerable.ForEach
apenas para redescobrir este último não existe. O C # perdeu um truque para facilitar as coisas aqui.Respostas:
Já existe uma declaração foreach incluída no idioma que faz o trabalho a maior parte do tempo.
Eu odiaria ver o seguinte:
Ao invés de:
O último é mais claro e fácil de ler na maioria das situações , embora talvez um pouco mais longo para digitar.
No entanto, devo admitir que mudei de posição sobre esse assunto; um método de extensão ForEach () seria realmente útil em algumas situações.
Aqui estão as principais diferenças entre a declaração e o método:
Esses são todos os pontos importantes apresentados por muitas pessoas aqui e posso ver por que as pessoas estão perdendo a função. Eu não me importaria de a Microsoft adicionar um método ForEach padrão na próxima iteração de estrutura.
fonte
foreach
é o tipo verificado no tempo de compilação se o enumerável estiver parametrizado. Falta apenas a verificação do tipo de tempo de compilação para coleções não genéricas, assim como umForEach
método de extensão hipotético .col.Where(...).OrderBy(...).ForEach(...)
. Sintaxe muito mais simples, sem poluição de tela com variáveis locais redundantes ou colchetes longos em foreach ().list.ForEach(item => item.DoSomething())
. E quem disse que é uma lista? O caso de uso óbvio está no final de uma cadeia; com a declaração foreach, você teria que colocar toda a cadeia depoisin
e a operação a ser executada dentro do bloco, o que é muito menos natural ou consistente.O método ForEach foi adicionado antes do LINQ. Se você adicionar a extensão ForEach, ela nunca será chamada para instâncias da Lista devido às restrições dos métodos de extensão. Eu acho que a razão de não ter sido adicionado é não interferir com o existente.
No entanto, se você realmente sentir falta dessa pequena função, poderá lançar sua própria versão
fonte
Você pode escrever este método de extensão:
Prós
Permite encadeamento:
Contras
Na verdade, não fará nada até você fazer algo para forçar a iteração. Por esse motivo, não deve ser chamado
.ForEach()
. Você poderia escrever.ToList()
no final ou também este método de extensão:Isso pode ser um desvio muito significativo das bibliotecas C # de remessa; leitores que não estão familiarizados com seus métodos de extensão não saberão o que fazer com seu código.
fonte
{foreach (var e in source) {action(e);} return source;}
numbers.Select(n => n*2);
Select()
diz aplicar uma função a cada elemento e retornar o valor da função ondeApply()
diz aplicar umAction
a cada elemento e retornar o elemento original .Select(e => { action(e); return e; })
A discussão aqui dá a resposta:
Basicamente, foi tomada a decisão de manter os métodos de extensão funcionalmente "puros". Um ForEach incentivaria efeitos colaterais ao usar os métodos de extensão Enumerable, que não eram a intenção.
fonte
Embora eu concorde que é melhor usar a construção
Exemploforeach
interna na maioria dos casos, acho que o uso dessa variação na extensão ForEach <> é um pouco melhor do que ter que gerenciar o índice regularmenteforeach
:Daria a você:
fonte
Uma solução alternativa é escrever
.ToList().ForEach(x => ...)
.profissionais
Fácil de entender - o leitor precisa apenas saber o que é fornecido com o C #, não quaisquer métodos de extensão adicionais.
O ruído sintático é muito leve (apenas adiciona um pouco de código extranioso).
Geralmente, não custa memória extra, pois um nativo
.ForEach()
precisaria realizar toda a coleção.contras
A ordem das operações não é ideal. Prefiro perceber um elemento, depois agir sobre ele e depois repetir. Esse código realiza todos os elementos primeiro e depois age sobre eles em sequência.
Se a realização da lista gera uma exceção, você nunca age em um único elemento.
Se a enumeração for infinita (como os números naturais), você estará sem sorte.
fonte
Enumerable.ForEach<T>
método, existe umList<T>.ForEach
método. c) "Geralmente não custa memória extra, pois um .ForEach () nativo precisaria realizar toda a coleção, de qualquer maneira." - absurdo completo e absoluto. Aqui é a implementação de Enumerable.ForEach <T> que C # daria se o fizesse:public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
Eu sempre me perguntei isso, é por isso que sempre carrego isso comigo:
Bom pequeno método de extensão.
fonte
Portanto, houve muitos comentários sobre o fato de um método de extensão ForEach não ser apropriado, pois não retorna um valor como os métodos de extensão LINQ. Embora essa seja uma afirmação factual, não é inteiramente verdade.
Todos os métodos de extensão LINQ retornam um valor para que possam ser encadeados:
No entanto, apenas porque o LINQ é implementado usando métodos de extensão não significa que os métodos de extensão devem ser usados da mesma maneira e retornar um valor. Escrever um método de extensão para expor funcionalidades comuns que não retornam um valor é um uso perfeitamente válido.
O argumento específico sobre o ForEach é que, com base nas restrições dos métodos de extensão (ou seja, que um método de extensão nunca substituirá um método herdado com a mesma assinatura ), pode haver uma situação em que o método de extensão personalizado esteja disponível em todas as classes que impelem IEnumerable
<T
> exceto Lista<T
>. Isso pode causar confusão quando os métodos começam a se comportar de maneira diferente, dependendo se o método de extensão ou o método de herança está sendo chamado ou não.fonte
Você pode usar o (encadeado, mas preguiçosamente avaliado)
Select
, primeiro fazendo sua operação e depois retornando a identidade (ou qualquer outra coisa, se preferir)Você precisará garantir que ele ainda seja avaliado, seja com
Count()
(a operação mais barata para enumerar afaik) ou com outra operação necessária.Eu adoraria vê-lo trazido para a biblioteca padrão:
O código acima torna-
people.WithLazySideEffect(p => Console.WriteLine(p))
se efetivamente equivalente ao foreach, mas preguiçoso e encadeado.fonte
Select
não faz mais o que deveria fazer. é definitivamente uma solução para o que o OP pede - mas sobre a legibilidade do tópico: ninguém esperaria que o select execute uma função.Select
não fizer o que deveria, isso é um problema com a implementaçãoSelect
, não com o código que chamaSelect
, que não tem influência sobre o queSelect
faz.Observe que o MoreLINQ NuGet fornece o
ForEach
método de extensão que você está procurando (bem como umPipe
método que executa o delegado e produz o resultado). Vejo:fonte
@Coincoin
O poder real do método de extensão foreach envolve a reutilização do
Action<>
sem adicionar métodos desnecessários ao seu código. Diga que você tem 10 listas e deseja executar a mesma lógica nelas, e uma função correspondente não se encaixa na sua classe e não é reutilizada. Em vez de ter dez para loops ou uma função genérica que obviamente é um auxiliar que não pertence, você pode manter toda a sua lógica em um só lugar (aAction<>
. Assim, dezenas de linhas são substituídas poretc ...
A lógica está em um só lugar e você não poluiu sua classe.
fonte
f(p)
, em seguida, deixar de fora a{ }
de taquigrafia:for (var p in List1.Concat(List2).Concat(List2)) f(p);
.A maioria dos métodos de extensão LINQ retorna resultados. O ForEach não se encaixa nesse padrão, pois não retorna nada.
fonte
public IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
Se você tiver F # (que estará na próxima versão do .NET), poderá usar
Seq.iter doSomething myIEnumerable
fonte
Parcialmente, é porque os designers da linguagem discordam de uma perspectiva filosófica.
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
fonte
Você pode usar select quando quiser retornar algo. Caso contrário, você pode usar o ToList primeiro, porque provavelmente não deseja modificar nada na coleção.
fonte
Eu escrevi um post sobre isso: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
Você pode votar aqui se quiser ver esse método no .NET 4.0: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
fonte
Na 3.5, todos os métodos de extensão adicionados ao IEnumerable estão disponíveis para suporte a LINQ (observe que eles estão definidos na classe System.Linq.Enumerable). Neste post, explico por que o foreach não pertence ao LINQ: método de extensão LINQ existente semelhante ao Parallel.For?
fonte
Sou eu ou é a Lista <T> .Foreach praticamente se tornou obsoleta pelo Linq. Originalmente havia
onde Y simplesmente tinha que ser IEnumerable (Pre 2.0) e implementar um GetEnumerator (). Se você olhar para o MSIL gerado, poderá ver que é exatamente o mesmo que
(Consulte http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx para o MSIL)
Então, no DotNet2.0 Generics, apareceu a lista. Foreach sempre me pareceu uma implementação do padrão Vistor (veja Design Patterns de Gamma, Helm, Johnson, Vlissides).
Agora, é claro que na versão 3.5, podemos usar um Lambda para o mesmo efeito; por exemplo, tente http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh- meu/
fonte
Eu gostaria de expandir a resposta de Aku .
Se você quiser chamar um método com o único objetivo de seu efeito colateral sem iterar todo o enumerável primeiro, poderá usar isso:
fonte
Select(x => { f(x); return x;})
Ninguém ainda apontou que o ForEach <T> resulta na verificação do tipo de tempo de compilação em que a palavra-chave foreach é verificada em tempo de execução.
Depois de fazer uma refatoração em que os dois métodos foram usados no código, sou a favor do .ForEach, pois tive que procurar falhas de teste / falhas de tempo de execução para encontrar os problemas do foreach.
fonte
foreach
tem menos tempo de compilação. Claro, se sua coleção for um IEnumerable não genérico, a variável de loop será umaobject
, mas o mesmo seria verdadeiro para umForEach
método de extensão hipotético .