foreach vs someList.ForEach () {}
primeiro tipo:
List<string> someList = <some way to init>
foreach(string s in someList) {
<process the string>
}
Para Outro Lado:
List<string> someList = <some way to init>
someList.ForEach(delegate(string s) {
<process the string>
});
Suponho que, do nada, em vez do delegado anónimo que uso acima, teria um delegado reutilizável que poderia especificar...
12 answers
for
para iterar a colecção, isto é válido (editar: antes de. Net 4.5 - a implementação mudou e ambos lançam):
someList.ForEach(x => { if(x.RemoveMe) someList.Remove(x); });
Considerando que foreach
usa um enumerador, por isso isto não é válido:
foreach(var item in someList)
if(item.RemoveMe) someList.Remove(item);
TL; dr: não copypaste este código em sua aplicação!
Estes exemplos não são as melhores práticas, são apenas para demonstrar as diferenças entreForEach()
e foreach
.
Remover itens de uma lista dentro de um laço for
pode ter efeitos secundários. O mais comum é descrito nos comentários a esta pergunta.
Geralmente, se você está procurando remover vários itens de uma lista, você gostaria de separar a determinação dos itens a remover da remoção real. Ele não mantém o seu código compacto, mas garante que você não perca nenhum item.
Tínhamos um código aqui (em VS2005 e C#2.0) onde os engenheiros anteriores se esforçaram para usar {[[0]} em vez de foreach(item in list) {foo; };
para todo o código que eles escreveram. por exemplo, um bloco de código para ler linhas de um leitor de dados.
list.ForEach()
são:
É mais descritivo em C # 2.0. No entanto, em C# 3 em diante, você pode usar a sintaxe "
=>
" para fazer um pouco mais suave expressao.É menos familiar. As pessoas que têm de manter este código vão perguntar-se porque o fizeste assim. Levei algum tempo para decidir que não havia nenhuma razão, exceto talvez para fazer o escritor parecer inteligente (a qualidade do resto do Código minou isso). Também era menos legível, com o "
})
" no final do bloco de código delegado.Veja Também o livro de Bill Wagner "Effective C#: 50 Specific Ways to Improve Your C#", onde ele fala sobre why foreach is preferred to other loops like for or while loops-the main point is that you are leaving the compiler decide the best way to construct the loop. Se uma versão futura do compilador conseguir usar uma técnica mais rápida, então você vai obter isso gratuitamente usando foreach e reconstrução, em vez de mudar o seu código.
Uma construção
foreach(item in list)
permite-lhe usarbreak
oucontinue
Se precisar de sair da iteração ou do ciclo. Mas você não pode alterar a lista dentro de um foreach loop.
list.ForEach
poderia ser mais rápido devido ao uso interno e um laço for
sem o invólucro seria ainda mais rápido.
Discordo que a versão list.ForEach(delegate)
seja" mais funcional " de alguma forma significativa. Ele passa uma função para uma função, mas não há grande diferença no resultado ou organização do programa.
Eu não acho que foreach(item in list)
"diz exatamente como você quer que seja feito" - um laço for(int 1 = 0; i < count; i++)
faz isso, um laço foreach
deixa a escolha do controle até o compilador.
O meu sentimento é, num novo projecto, usar foreach(item in list)
para a maioria dos loops, a fim de aderir ao uso comum e para a legibilidade, e usar list.Foreach()
apenas para blocos curtos, quando você pode fazer algo mais elegante ou compactamente com o operador C# 3 "=>
". Em casos como este, já pode haver um método de extensão LINQ que é mais específico do que ForEach()
. Veja se Where()
, Select()
, Any()
, All()
, Max()
ou um dos muitos outros métodos LINQ já não faz o que você quer do loop.
public void ForEach(Action<T> action)
{
if (action == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
for (int i = 0; i < this._size; i++)
{
action(this._items[i]);
}
}
Similarmente, o motivo no enumerador que é o que é usado por foreach é este:
public bool MoveNext()
{
if (this.version != this.list._version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
if (this.index < this.list._size)
{
this.current = this.list._items[this.index];
this.index++;
return true;
}
this.index = this.list._size + 1;
this.current = default(T);
return false;
}
A Lista.ForEach is much more trimmed down than MoveNext-far less processing-will more likely JIT into something efficient..
Além disso, foreach() irá alocar um novo enumerador não importa o que aconteça. O GC é seu amigo, mas se você está fazendo o mesmo foreach repetidamente, isso vai fazer mais objetos descartáveis, ao contrário de reutilizar o mesmo delegado - Mas - Este é realmente um caso marginal. No uso típico você verá pouca ou nenhuma diferença.
// A list of actions to execute later
List<Action> actions = new List<Action>();
// Numbers 0 to 9
List<int> numbers = Enumerable.Range(0, 10).ToList();
// Store an action that prints each number (WRONG!)
foreach (int number in numbers)
actions.Add(() => Console.WriteLine(number));
// Run the actions, we actually print 10 copies of "9"
foreach (Action action in actions)
action();
// So try again
actions.Clear();
// Store an action that prints each number (RIGHT!)
numbers.ForEach(number =>
actions.Add(() => Console.WriteLine(number)));
// Run the actions
foreach (Action action in actions)
action();
A Lista.O método ForEach não tem este problema. O item atual da iteração é passado pelo valor como um argumento para a lambda exterior, e então a lambda interior captura corretamente esse argumento em seu próprio encerramento. Problema resolvido.
{[[2]}(infelizmente eu acredito que ForEach é um membro da lista, ao invés de um método de extensão, embora seja fácil defini-lo você mesmo para que você tenha esta facilidade em qualquer tipo enumerável.)Em segundo lugar, a abordagem de cada método tem uma limitação. Se você está implementando IEnumerable usando yield return, você não pode fazer um yield return dentro do lambda. Então fazer looping através dos itens em uma coleção, a fim de produzir coisas de retorno não é possível por este método. Você terá para usar a palavra-chave foreach e trabalhar em torno do problema de fechamento, fazendo manualmente uma cópia do valor do laço atual dentro do laço.
Acho que a chamada someList.ForEach()
pode ser facilmente paralela, enquanto que a normal foreach
não é assim tão fácil de executar paralela.
Você poderia facilmente executar vários delegados diferentes em núcleos diferentes, o que não é tão fácil de fazer com um normal foreach
.
Só os meus 2 cêntimos.
Podias nomear o delegado anónimo: -)
E podes escrever o segundo como:
someList.ForEach(s => s.ToUpper())
O que eu prefiro, e poupa muita digitação.
Como diz Joachim, o paralelismo é mais fácil de aplicar à segunda forma.
Lista.ForEach() é considerado mais funcional.
List.ForEach()
diz o que queres que seja feito. Também diz exactamente como queres que seja feito. Isso deixa List.ForEach
livre para alterar a implementação do como parte no futuro. Por exemplo, uma hipotética versão futura do.Net pode sempre executar List.ForEach
em paralelo, sob a suposição de que neste ponto todo mundo tem um número de núcleos de cpu que geralmente estão sentados ociosos.
Por outro lado, foreach (item in list)
Dá-te um um pouco mais de controlo sobre o laço. Por exemplo, você sabe que os itens serão iterados em algum tipo de ordem sequencial, e você pode facilmente quebrar no meio se um item atender a alguma condição.
Http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx
A segunda maneira que você mostrou usa um método de extensão para executar o método de delegado para cada um dos elementos da lista.
Desta forma, você tem outra chamada de delegado (=método).
Além disso, existe a possibilidade de iterar a lista com um para loop.
A função ForEach é membro da lista de classes genérica.
Criei a seguinte extensão para reproduzir o código interno:
public static class MyExtension<T>
{
public static void MyForEach(this IEnumerable<T> collection, Action<T> action)
{
foreach (T item in collection)
action.Invoke(item);
}
}
Então a extremidade nós estamos usando um foreach normal (ou um laço para se você quiser).
Por outro lado, usar uma função delegada é apenas outra forma de definir uma função, este código:
delegate(string s) {
<process the string>
}
É equivalente a:
private static void myFunction(string s, <other variables...>)
{
<process the string>
}
Ou usando expressões labda:
(s) => <process the string>
Todo o âmbito ForEach (função delegada) é tratado como uma única linha de código (chamando a função), e você não pode definir pontos de paragem ou entrar no código. Se ocorrer uma excepção não tratada, o bloco inteiro é marcado.