foreach vs someList.ForEach () {}

Aparentemente, há muitas formas de iterar uma colecção. Curioso se há alguma diferença, ou por que você usaria uma maneira sobre a outra.

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...

Author: Peter Bruins, 2008-10-22

12 answers

Há uma distinção importante e útil entre as duas. Porque sim .ForEach usa um laço 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 entre ForEach() 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.

 105
Author: Will, 2016-06-30 13:16:11

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.

Ainda não sei exactamente porque fizeram isto. As desvantagens de 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 usar break ou continue Se precisar de sair da iteração ou do ciclo. Mas você não pode alterar a lista dentro de um foreach loop.

Surpreende-me ver que é um pouco mais rápido. Mas isso provavelmente não é uma razão válida para usá-lo por todo o lado , isso seria uma otimização prematura. Se a sua aplicação usa um banco de dados ou serviço web que, não o controle de loop, vai quase sempre ser onde o tempo vai. E também a comparaste com um laço? O 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.

 61
Author: Anthony, 2012-04-04 14:03:07
Por Diversão, coloquei a lista no reflector e este é o resultado C#:
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.

 16
Author: plinth, 2008-10-22 14:53:48
Conheço duas coisas obscuras que as tornam diferentes. Vai-te a mim! Em primeiro lugar, há o erro clássico de fazer um delegado para cada item da lista. Se você usar o prefácio foreach, todos os seus delegados podem acabar se referindo ao último item da lista:
    // 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.

Mais aqui

 11
Author: Daniel Earwicker, 2008-10-22 15:41:40

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.

 11
Author: Joachim Kerschbaumer, 2018-03-23 09:57:12

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.
 3
Author: Craig.Nicol, 2008-10-22 14:24:52

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.

 3
Author: Joel Coehoorn, 2008-10-22 14:54:23
[[1]}nos bastidores, o delegado anónimo torna-se um método real para que você possa ter alguma sobrecarga com a segunda escolha se o compilador não optar por alinhar a função. Adicionalmente, quaisquer variáveis locais referenciadas pelo corpo do exemplo de delegado anônimo mudariam na natureza por causa de Truques de compilador para esconder o fato de que ele é compilado para um novo método. Mais informações aqui sobre como C# faz isso magia:

Http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx

 2
Author: jezell, 2008-10-22 14:26:44

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.

 1
Author: EFrank, 2008-10-22 14:25:43
Uma coisa a ter cuidado é como sair do Genérico .ForEach method-see this discussion . Embora a ligação pareça dizer que este caminho é o mais rápido. Não sei porque ... seria de esperar que fossem equivalentes uma vez compilados...
 1
Author: Chris Kimpton, 2008-10-22 14:35:55

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>
 1
Author: Pablo Caballero, 2014-04-25 10:50:04

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.

 1
Author: Peter Shen, 2016-03-31 06:49:57