Frage

  

Mögliche Duplizieren:
  Warum gibt es keine Methode ForEach Erweiterung auf der Schnittstelle IEnumerable?

Ich habe bemerkt, wenn LINQ-y Code schreiben, .ForEach() eine schöne Idiom zu bedienen ist. Zum Beispiel, hier ist ein Stück Code, der die folgenden Eingaben nimmt und produziert diese Ausgänge:

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

Und der Code:

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

Beachten Sie die warf ein .ToList() vor dem .ForEach() auf der zweiten Linie zu halten.

Warum ist es, dass .ForEach() als eine Erweiterungsmethode auf IEnumerable<T> nicht verfügbar? Mit einem Beispiel so scheint es, nur seltsam.

War es hilfreich?

Lösung

Da ForEach(Action) existierte, bevor IEnumerable<T> existierte.

Da es nicht mit den anderen Erweiterungsmethoden hinzugefügt wurde, kann man davon ausgehen, dass die C # Designer fühlten es war ein schlechtes Design und bevorzugt das foreach Konstrukt.


Edit:

Wenn Sie möchten, können Sie Ihre eigene Erweiterungsmethode erstellen, es wird den einen für eine List<T> nicht außer Kraft setzen, aber es wird für jede andere Klasse zu arbeiten, die IEnumerable<T> implementiert.

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

Andere Tipps

Laut Eric Lippert, dann ist dies meist aus philosophischen Gründen . Sie sollten den ganzen Beitrag lesen, aber hier ist der Kern so weit es mich betrifft:

  

Ich bin philosophischer Gegensatz zu   die Bereitstellung eines solchen Verfahrens, für zwei   Gründe.

     

Der erste Grund ist, dass dabei   verstößt gegen die funktionale Programmierung   Prinzipien, die alle andere Sequenz   Betreiber basieren auf. klar, dass die   alleinigen Zweck eines Aufruf dieser Methode   Nebenwirkungen verursachen wird.

     

Der Zweck eines Ausdrucks ist zu   berechnen einen Wert, nicht eine Seite zu veranlassen   bewirken. Der Zweck einer Aussage   eine Nebenwirkung zu verursachen. Der Aufrufort   dieser Sache würde eine Menge aussehen   wie ein Ausdruck (obwohl,   Zugegeben, da das Verfahren ist   hohlraum Rückkehr, könnte der Ausdruck   nur in einer „Anweisung verwendet werden   Ausdruck“Kontext.)

     

Es sitzt nicht gut mit mir zu machen   die einzige Sequenz Operator   das ist nur nützlich für die Seite   Effekte.

     

Der zweite Grund ist, dass dabei   fügt Null neue Darstellungsleistung   auf die Sprache.

Da ForEach() auf einem IEnumerable ist nur ein normaler für jede Schleife wie folgt:

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

Ich bin zu raten, gerade hier, aber foreach auf IEnumerable setzen würde Operationen darauf macht Nebenwirkungen zu haben. Keiner der „verfügbar“ Erweiterungsmethoden verursachen Nebenwirkungen, eine dringend notwendige Methode wie foreach setzte dort das api trüben würde, denke ich. Auch foreach würde initialisieren faul Sammlung.

persönlich die Versuchung abwehren nur meine eigenen hinzufügen, nur ich habe frei von Nebenwirkungen Funktionen getrennt von denen mit Nebenwirkungen zu halten.

ForEach ist nicht auf IList es auf Liste ist. Sie wurden mit der konkreten Liste in Ihrem Beispiel.

Ich weiß nicht, ehrlich sicher, warum die .ForEach (Action) ist nicht auf IEnumerable enthalten, sondern, richtig, falsch oder gleichgültig, das ist so wie es ist ...

I DID jedoch will das Leistungsproblem in anderen Kommentaren erwähnt markieren. Es ist eine Performance-Einbußen auf, wie Sie Schleife über eine Sammlung. Es ist relativ gering, aber dennoch, es besteht sicherlich. Hier ist ein unglaublich schnell und schlampig Code-Schnipsel, die Beziehungen zu zeigen ... dauert nur eine Minute oder so durchlaufen.

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

Lassen Sie sich nicht, obwohl auf diese zu viel aufgehängt. Performance ist die Währung von Anwendungsdesign, aber es sei denn, Ihre Anwendung ist eine tatsächliche Leistungseinbußen auftreten, die Usability-Probleme verursacht, den Schwerpunkt auf die Codierung für die Wartbarkeit und die Wiederverwendung seit der Zeit die Währung des wirklichen Lebens Business-Projekten ist ...

ForEach ist in der konkreten Klasse List<T> implementiert

Nur eine Vermutung, aber Liste kann seine Produkte durchlaufen, ohne einen Enumerator zu erstellen:

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

Dies kann zu einer besseren Leistung führen. Mit IEnumerable, haben Sie nicht die Möglichkeit, eine gewöhnliche for-Schleife zu verwenden.

Es ist "Select" auf IEnumerable<T> genannt Ich bin erleuchtet, danke.

LINQ folgt dem Pull-Modell und alle seine (Erweiterung) Methoden sollten IEnumerable<T> zurückkehren, mit Ausnahme von ToList(). Die ToList() ist es die Pull-Kette zu beenden.

ForEach() ist aus der Push-Modell Welt.

Sie können noch Ihre eigene Erweiterungsmethode schreiben, dies zu tun, wie von Samuel hingewiesen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top