Pergunta

Duplicate possíveis:
Por que não há um método de extensão ForEach na interface IEnumerable?

Tenho notado ao escrever código LINQ-y que .ForEach() é uma boa expressão para uso. Por exemplo, aqui é um pedaço de código que leva as seguintes entradas e produz estas saídas:

{ "One" } => "One"
{ "One", "Two" } => "One, Two"
{ "One", "Two", "Three", "Four" } => "One, Two, Three and Four";

E o código:

private string InsertCommasAttempt(IEnumerable<string> words)
{
    List<string> wordList = words.ToList();
    StringBuilder sb = new StringBuilder();
    var wordsAndSeparators = wordList.Select((string word, int pos) =>
        {
            if (pos == 0) return new { Word = word, Leading = string.Empty };
            if (pos == wordList.Count - 1) return new { Word = word, Leading = " and " };
            return new { Word = word, Leading = ", " };
        });

    wordsAndSeparators.ToList().ForEach(v => sb.Append(v.Leading).Append(v.Word));
    return sb.ToString();
}

Observe o .ToList() interveio antes da .ForEach() na segunda à última linha.

Por que é que .ForEach() não está disponível como um método de extensão em IEnumerable<T>? Com um exemplo como este, ele só parece estranho.

Foi útil?

Solução

ForEach(Action) existia antes IEnumerable<T> existiu.

Uma vez que não foi adicionado com os outros métodos de extensão, pode-se supor que os C # projetistas sentiram que era uma má concepção e preferem a construção foreach.


Editar:

Se você quiser, pode criar o seu próprio método de extensão, ele não vai substituir o um para um List<T> mas ele vai trabalhar para qualquer outra classe que implementa IEnumerable<T>.

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

Outras dicas

De acordo com Eric Lippert, este é href="http://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx" principalmente por razões filosóficas . Você deve ler todo o post, mas aqui é a essência tanto quanto eu estou em causa:

Estou filosoficamente contrário de proporcionando um tal método, para dois razões.

A primeira razão é que isso viola a programação funcional princípios que toda a outra sequência operadores são baseadas. É evidente que o único propósito de uma chamada para este método é a efeitos colaterais de causa.

O objetivo de uma expressão é calcular um valor, para não causar um lado efeito. O propósito de uma declaração é para causar um efeito colateral. O site de chamada desta coisa seria uma enorme quantidade como uma expressão (embora, É certo que, uma vez que o método é vazio-retorno, a expressão poderia só pode ser usado em uma “declaração expressão”contexto.)

Ele não se sente bem comigo para fazer o primeiro e único operador sequência que só é útil para o seu lado efeitos.

A segunda razão é que isso acrescenta zero, novo poder representacional à linguagem.

Porque ForEach() em um IEnumerable é apenas um normal para cada loop como este:

for each T item in MyEnumerable
{
    // Action<T> goes here
}

Eu estou apenas supondo aqui, mas colocando foreach em IEnumerable faria operações nele para ter efeitos secundários. Nenhum dos métodos de extensão "disponíveis" causar efeitos secundários, colocando um método imperativo como foreach lá seria turvar a api eu acho. Além disso, foreach seria inicializar a coleção preguiçoso.

Pessoalmente eu fui cortar a tentação de apenas acrescentar o meu próprio, apenas para manter efeito colateral livre funções separar aqueles com efeitos colaterais.

ForEach não está na IList é na lista. Você estava usando a Lista de concreto no seu exemplo.

Eu honestamente não sei ao certo por que o .ForEach (Ação) não está incluído no IEnumerable, mas, certo, errado ou indiferente, que é a maneira que é ...

No entanto, eu quero destacar o problema de desempenho mencionado em outros comentários. Há um acerto de desempenho com base em como você loop sobre uma coleção. É relativamente menor, mas mesmo assim, ele certamente existe. Aqui está um trecho de código incrivelmente rápido e malfeito para mostrar as relações ... leva apenas um minuto ou mais para percorrer.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Start Loop timing test: loading collection...");
        List<int> l = new List<int>();

        for (long i = 0; i < 60000000; i++)
        {
            l.Add(Convert.ToInt32(i));
        }

        Console.WriteLine("Collection loaded with {0} elements: start timings",l.Count());
        Console.WriteLine("\n<===============================================>\n");
        Console.WriteLine("foreach loop test starting...");

        DateTime start = DateTime.Now;

        //l.ForEach(x => l[x].ToString());

        foreach (int x in l)
            l[x].ToString();

        Console.WriteLine("foreach Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");
        Console.WriteLine("List.ForEach(x => x.action) loop test starting...");

        start = DateTime.Now;

        l.ForEach(x => l[x].ToString());

        Console.WriteLine("List.ForEach(x => x.action) Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");

        Console.WriteLine("for loop test starting...");

        start = DateTime.Now;
        int count = l.Count();
        for (int i = 0; i < count; i++)
        {
            l[i].ToString();
        }

        Console.WriteLine("for Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");

        Console.WriteLine("\n\nPress Enter to continue...");
        Console.ReadLine();
    }

Não fique preso a este muito embora. Desempenho é a moeda do design do aplicativo, mas a menos que seu aplicativo está experimentando um acerto de desempenho real que está causando problemas de usabilidade, foco na codificação para manutenção e reutilização desde o tempo é a moeda de projectos empresariais vida real ...

ForEach é implementado no List<T> classe concreta

Apenas um palpite, mas lista pode iterar sobre seus itens sem criar um enumerador:

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]);
    }
}

Isso pode levar a um melhor desempenho. Com IEnumerable, você não tem a opção de usar um ordinário for-loop.

É chamado de "Select" na IEnumerable<T> Eu estou iluminado, obrigado.

LINQ segue o pull-modelo e todas as suas (extensão) métodos devem retornar IEnumerable<T>, exceto para ToList(). O ToList() está lá para terminar a cadeia de puxar.

ForEach() é do mundo do push-modelo.

Você ainda pode escrever seu próprio método de extensão para fazer isso, como fora apontado por Samuel.

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