Domanda

  

Possibile duplicato:
   Perché non esiste un metodo di estensione ForEach sull'interfaccia IEnumerable?

Quando ho scritto il codice LINQ-y ho notato che .ForEach () è un bel linguaggio da usare. Ad esempio, ecco un pezzo di codice che accetta i seguenti input e produce questi output:

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

E il codice:

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

Nota il .ToList () interposto prima del .ForEach () dalla penultima riga.

Perché .ForEach () non è disponibile come metodo di estensione su IEnumerable < T > ? Con un esempio come questo, sembra strano.

È stato utile?

Soluzione

Perché ForEach (Action) esisteva prima dell'esistenza di IEnumerable < T > .

Dato che non è stato aggiunto con altri metodi di estensione, si può presumere che i progettisti di C # abbiano ritenuto che fosse un cattivo design e preferissero il costrutto foreach .


Modifica:

Se vuoi puoi creare il tuo metodo di estensione, non sovrascriverà quello per un Elenco < T > ma funzionerà per qualsiasi altra classe che 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);
  }
}

Altri suggerimenti

Secondo Eric Lippert, questo è principalmente per motivi filosofici . Dovresti leggere l'intero post, ma ecco l'essenza per quanto mi riguarda:

  

Sono filosoficamente contrario   fornendo un tale metodo, per due   ragioni.

     

Il primo motivo è che così facendo   viola la programmazione funzionale   principi che tutte le altre sequenze   gli operatori sono basati su. Chiaramente il   unico scopo di una chiamata a questo metodo   è causare effetti collaterali.

     

Lo scopo di un'espressione è   calcolare un valore, per non causare un lato   effetto. Lo scopo di una dichiarazione è   per causare un effetto collaterale. Il sito di chiamata   di questa cosa sembrerebbe molto terribile   come un'espressione (sebbene,   certamente, poiché il metodo è   ritorno del vuoto, l'espressione potrebbe   può essere utilizzato solo in un'istruzione & # 8220 ;.   espressione & # 8221; contesto.)

     

Non mi piace stare con me   l'unico e unico operatore di sequenza   questo è utile solo per il suo lato   effetti.

     

Il secondo motivo è che così facendo   aggiunge zero nuovo potere rappresentativo   nella lingua.

Perché ForEach () su un IEnumerable è semplicemente normale per ogni ciclo in questo modo:

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

Sto solo indovinando qui, ma mettere foreach su IEnumerable farebbe operazioni su di esso per avere effetti collaterali. Nessuno dei "disponibili" i metodi di estensione causano effetti collaterali, mettere un metodo imperativo come foreach su lì confonderebbe l'ape immagino. Inoltre, foreach inizializzerebbe la collezione pigra.

Personalmente ho respinto la tentazione di aggiungere solo le mie, solo per mantenere le funzioni libere dagli effetti collaterali separate da quelle con effetti collaterali.

ForEach non è su IList è su List. Stavi usando l'Elenco concreto nel tuo esempio.

Onestamente non so con certezza perché il .ForEach (Azione) non sia incluso in IEnumerable ma, giusto, sbagliato o indifferente, è così ...

Ho comunque voluto evidenziare il problema di prestazioni menzionato in altri commenti. C'è un successo prestazionale basato sul modo in cui esegui il ciclo su una raccolta. È relativamente minore, ma esiste sicuramente. Ecco uno snippet di codice incredibilmente veloce e sciatto per mostrare le relazioni ... ci vuole solo un minuto per scorrere.

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

Non preoccuparti troppo di questo però. Le prestazioni sono la valuta del design dell'applicazione ma, a meno che l'applicazione non stia riscontrando un vero e proprio successo nelle prestazioni che causa problemi di usabilità, concentrati sulla codifica per manutenibilità e riutilizzo poiché il tempo è la valuta dei progetti di business della vita reale ...

ForEach è implementato nella classe concreta List<T>

Solo un'ipotesi, ma List può scorrere gli elementi senza creare un enumeratore:

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

Questo può portare a prestazioni migliori. Con IEnumerable, non hai la possibilità di usare un normale for-loop.

Si chiama " Seleziona " su IEnumerable<T> Sono illuminato, grazie.

LINQ segue il modello pull e tutti i suoi metodi (di estensione) dovrebbero restituire IEnumerable < T > , ad eccezione di ToList () . Il ToList () è lì per terminare la catena di pull.

ForEach () proviene dal mondo del modello push.

Puoi ancora scrivere il tuo metodo di estensione per farlo, come sottolineato da Samuel.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top