Frage

Ich möchte das Äquivalent der folgenden in LINQ tun, aber ich kann nicht herausfinden, wie:

IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());

Was ist die eigentliche Syntax?

War es hilfreich?

Lösung

Es gibt keine ForEach-Erweiterung für IEnumerable; nur für List<T>. So könnten Sie tun,

items.ToList().ForEach(i => i.DoStuff());

Alternativ schreiben Sie Ihre eigene ForEach-Extension-Methode:

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

Andere Tipps

Fredrik hat das Update zur Verfügung gestellt, aber es kann eine Überlegung wert sein, warum dies nicht im Rahmen ist mit zu beginnen. Ich glaube, die Idee ist, dass die LINQ-Abfrage Betreiber sollten nebenwirkungsfrei sein, mit einer einigermaßen funktional passend in der der Welt zu betrachten. Offensichtlich ForEach ist genau das Gegenteil - a. rein Nebeneffekt basierendes Konstrukt

Das ist nicht zu sagen, dass dies eine schlechte Sache zu tun -. Nur Gedanken über die philosophischen Gründe für die Entscheidung

Aktualisieren 2012.07.17: Offenbar als von C # 5.0, das Verhalten von foreach unten beschrieben worden, hat sich verändert und " die Verwendung einer foreach Iterationsvariable in einem verschachtelten Lambda-Ausdruck nicht mehr produziert unerwartete Ergebnisse. " Diese Antwort nicht C # ≥ 5,0 anzuwenden.

@ John Skeet und jeder, der das foreach Schlüsselwort vorzieht.

Das Problem mit "foreach" in C # vor 5.0 , ist, dass es nicht mit, wie das äquivalent‚für das Verständnis‘funktioniert in anderen Sprachen, und mit wie ich erwarten würde es (persönliche Meinung zu arbeiten hier nur angegeben, weil andere haben ihre Meinung in Bezug auf Lesbarkeit erwähnt). Jetzt die Fragen „ Zugriff auf modifizierte Schließung “ über sowie "

[Test]
public void ForEachExtensionWin()
{
    //Yes, I know there is an Observable.Range.
    var values = Enumerable.Range(0, 10);

    var observable = Observable.Create<Func<int>>(source =>
                            {
                                values.ForEach(value => 
                                    source.OnNext(() => value));

                                source.OnCompleted();
                                return () => { };
                            });

    //Simulate subscribing and evaluating Funcs
    var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();

    //Win
    Assert.That(evaluatedObservable, 
        Is.EquivalentTo(values.ToList()));
}

In der folgenden schlägt mit dem Fehler:

Erwartet: entspricht <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>    Aber war: <9, 9, 9, 9, 9, 9, 9, 9, 9, 9>

[Test]
public void ForEachKeywordFail()
{
    //Yes, I know there is an Observable.Range.
    var values = Enumerable.Range(0, 10);

    var observable = Observable.Create<Func<int>>(source =>
                            {
                                foreach (var value in values)
                                {
                                    //If you have resharper, notice the warning
                                    source.OnNext(() => value);
                                }
                                source.OnCompleted();
                                return () => { };
                            });

    //Simulate subscribing and evaluating Funcs
    var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();

    //Fail
    Assert.That(evaluatedObservable, 
        Is.EquivalentTo(values.ToList()));
}

Sie könnten die FirstOrDefault() Erweiterung verwenden, die für IEnumerable<T> zur Verfügung steht. Durch die Rückgabe false vom Prädikat, wird es für jedes Element ausgeführt werden, aber sich nicht darum kümmern, dass es nicht wirklich eine Übereinstimmung zu finden ist. Dadurch wird die ToList() Overhead vermeiden.

IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });

Ich nahm Fredriks Methode und modifiziert, um den Rückgabetyp.

Auf diese Weise werden die Verfahren unterstützt verzögerte Ausführung wie andere LINQ Methoden.

EDIT: Wenn das nicht klar ist, jede Nutzung dieser Methode muss mit ToList () oder anderer Art und Weise beenden, das Verfahren zu zwingen, auf dem vollständigen zu arbeiten abzählbar. Andernfalls würde die Aktion nicht ausgeführt werden!

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
    foreach (T item in enumeration)
    {
        action(item);
        yield return item;
    }
}

Und hier ist der Test zu helfen, sieht es:

[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
    IEnumerable<char> enumerable = new[] {'a', 'b', 'c'};

    var sb = new StringBuilder();

    enumerable
        .ForEach(c => sb.Append("1"))
        .ForEach(c => sb.Append("2"))
        .ToList();

    Assert.That(sb.ToString(), Is.EqualTo("121212"));
}

Wenn Sie den ToList () am Ende entfernen, werden Sie den Test sehen versagt, da der String eine leere Zeichenfolge enthält. Dies liegt daran, keine Methode, die ForEach gezwungen aufzuzählen.

Halten Sie Ihre Nebenwirkungen aus meinem IEnumerable

  
    

Ich möchte das Äquivalent der folgenden in LINQ tun, aber ich kann nicht herausfinden, wie:

  

Wie andere haben darauf hingewiesen hier und Ausland LINQ und IEnumerable Methoden erwartet werden Nebeneffekt, frei zu sein.

Möchten Sie wirklich „etwas tun“ wollen jedes Element in der IEnumerable? Dann foreach ist die beste Wahl. Die Menschen sind nicht überrascht, als Nebenwirkungen hier passieren.

foreach (var i in items) i.DoStuff();

Ich wette Sie keinen Nebeneffekt

wollen

Jedoch in meiner Erfahrung Nebenwirkungen sind in der Regel nicht erforderlich. Mehr als oft nicht, dass es eine einfache LINQ-Abfrage wartet darauf, von entweder Jon Skeet, Eric Lippert, oder Marc GRA durch eine StackOverflow.com Antwort begleitet, entdeckt zu werden erklären, wie, was Sie wollen tun!

Einige Beispiele

Wenn Sie tatsächlich nur aggregiert (Akkumulieren) einen gewissen Wert, dann sollten Sie die Aggregate Erweiterungsmethode in Betracht ziehen.

items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));

Vielleicht möchten Sie eine neue IEnumerable aus den vorhandenen Werten erstellen.

items.Select(x => Transform(x));

Oder vielleicht möchten Sie eine Nachschlagtabelle erstellen:

items.ToLookup(x, x => GetTheKey(x))

Die Liste (Wortspiel nicht ganz beabsichtigt) die Möglichkeiten geht weiter und weiter.

Wenn Sie möchten, handeln, wie die Aufzählung Rollen sollten Sie jedes Element erhalten.

public static class EnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
    {
        foreach (var item in enumeration)
        {
            action(item);
            yield return item;
        }
    }
}

Es ist eine experimentelle Freisetzung von Microsoft von Interactive Erweiterungen zu LINQ (auch auf NuGet , a href finden Sie unter <= "https: // www .nuget.org / profiles / rxteam“rel = "noreferrer"> RxTeams Profil für mehr Links). Die Channel 9 Video es gut erklärt .

Die Dokumente werden nur im XML-Format zur Verfügung gestellt. Ich habe diese Dokumentation in Sandcastle laufen, damit sie in einem besser lesbaren Format sein. Entpacken Sie das Archiv Dokumente und suchen Sie nach index.html .

Unter den vielen anderen guten Sachen, bietet es die erwartete FürJeden Umsetzung. Es ermöglicht Ihnen, Code wie folgt zu schreiben:

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };

numbers.ForEach(x => Console.WriteLine(x*x));

Nach PLINQ (verfügbar seit .NET 4.0), können Sie ein

IEnumerable<T>.AsParallel().ForAll() 

eine parallel foreach Schleife auf einem IEnumerable zu tun.

Der Zweck der ForEach ist zu Nebenwirkungen führen. IEnumerable ist für faule Aufzählung eines Satzes.

Dieser konzeptionelle Unterschied ist deutlich sichtbar, wenn Sie es in Betracht ziehen.

SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));

Dies wird nicht ausgeführt, bis Sie einen „count“ oder einen „ToList ()“ oder etwas dagegen tun. Es ist eindeutig nicht das, was ausgedrückt wird.

Sie sollten die IEnumerable Erweiterungen verwenden für Ketten von Iteration einrichten, von ihren jeweiligen Quellen und Bedingungen definining Inhalt. Expression Bäume sind leistungsstark und effizient, aber Sie sollten ihre Natur lernen zu schätzen. Und das nicht nur für die Programmierung um sich herum ein paar Zeichen zu speichern lazy evaluation überschreibt.

Viele Menschen erwähnt, aber ich hatte es aufzuschreiben. Ist das nicht die meisten klar / lesbarsten?

IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();

Kurz und knapp (st).

Wie zahlreiche Antworten schon darauf hinweisen, können Sie ganz einfach hinzufügen, eine solche Erweiterung Methode selbst. Wenn Sie jedoch nicht das tun wollen, auch wenn ich von etwas in der BCL nicht bewusst bin, gibt es immer noch eine Option im System Namespace, wenn Sie bereits einen Verweis auf eine href haben <= "https: / /www.nuget.org/packages/Rx-Main“rel = "nofollow noreferrer"> Reactive Erweiterung (und wenn Sie nicht tun, sollten Sie haben):

using System.Reactive.Linq;

items.ToObservable().Subscribe(i => i.DoStuff());

Obwohl die Methodennamen ein bisschen unterschiedlich sind, das Endergebnis ist genau das, was Sie suchen.

Jetzt haben wir die Möglichkeit, ...

        ParallelOptions parallelOptions = new ParallelOptions();
        parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
        parallelOptions.MaxDegreeOfParallelism = 1;
#endif
        Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));

Natürlich ist dies eröffnet eine ganz neue Dose Faden- auf.

ps (Leider über die Schriften, ist es, was das System entschieden)

Dieses „funktionalen Ansatz“ Abstraktion Lecks große Zeit. Nichts auf der Sprachebene verhindert Nebenwirkungen. Solange Sie können es Ihr Lambda / Delegaten in dem Behälter für jedes Element machen nennen -. Sie werden das „ForEach“ Verhalten bekommen

Hier zum Beispiel eine Möglichkeit, srcDictionary in destDictionary der Verschmelzung (falls Schlüssel bereits vorhanden ist - überschreibt)

dies ist ein Hack, und soll nicht in jedem Produktionscode verwendet werden.

var b = srcDictionary.Select(
                             x=>
                                {
                                  destDictionary[x.Key] = x.Value;
                                  return true;
                                }
                             ).Count();

Inspiriert von Jon Skeet, ich habe seine Lösung mit dem folgenden erweitert:

Erweiterungsmethode:

public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector)
{
    foreach (var item in source)
    {
        var target = keySelector(item);
        applyBehavior(target);
    }
}

Auftraggeber:

var jobs = new List<Job>() 
    { 
        new Job { Id = "XAML Developer" }, 
        new Job { Id = "Assassin" }, 
        new Job { Id = "Narco Trafficker" }
    };

jobs.Execute(ApplyFilter, j => j.Id);

. . .

    public void ApplyFilter(string filterId)
    {
        Debug.WriteLine(filterId);
    }

ForEach kann auch Chained , nur um die pileline setzen nach der Aktion. bleiben fließend


Employees.ForEach(e=>e.Act_A)
         .ForEach(e=>e.Act_B)
         .ForEach(e=>e.Act_C);

Orders  //just for demo
    .ForEach(o=> o.EmailBuyer() )
    .ForEach(o=> o.ProcessBilling() )
    .ForEach(o=> o.ProcessShipping());


//conditional
Employees
    .ForEach(e=> {  if(e.Salary<1000) e.Raise(0.10);})
    .ForEach(e=> {  if(e.Age   >70  ) e.Retire();});

Ein Eager Version der Umsetzung.

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (T item in enu) action(item);
    return enu; // make action Chainable/Fluent
}

Edit: Faule Version wird mit yield return, wie diesem .

public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (var item in enu)
    {
        action(item);
        yield return item;
    }
}

The Lazy Version Anforderungen materialisiert, ToList () zum Beispiel sein, sonst passiert nichts. siehe unten große Kommentare von ToolmakerSteve.

IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
    .ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();

Ich halte beide foreach () und ForEachLazy () in meiner Bibliothek.

Ich respectually mit dem Begriff nicht einverstanden ist, die Erweiterungsmethoden verknüpfen sollte Nebeneffekt frei sein (nicht nur, weil sie es nicht sind, kann jede Delegierte Nebenwirkungen durchführt).

Beachten Sie Folgendes:

   public class Element {}

   public Enum ProcessType
   {
      This = 0, That = 1, SomethingElse = 2
   }

   public class Class1
   {
      private Dictionary<ProcessType, Action<Element>> actions = 
         new Dictionary<ProcessType,Action<Element>>();

      public Class1()
      {
         actions.Add( ProcessType.This, DoThis );
         actions.Add( ProcessType.That, DoThat );
         actions.Add( ProcessType.SomethingElse, DoSomethingElse );
      }

      // Element actions:

      // This example defines 3 distict actions
      // that can be applied to individual elements,
      // But for the sake of the argument, make
      // no assumption about how many distict
      // actions there may, and that there could
      // possibly be many more.

      public void DoThis( Element element )
      {
         // Do something to element
      }

      public void DoThat( Element element )
      {
         // Do something to element
      }

      public void DoSomethingElse( Element element )
      {
         // Do something to element
      }

      public void Apply( ProcessType processType, IEnumerable<Element> elements )
      {
         Action<Element> action = null;
         if( ! actions.TryGetValue( processType, out action ) )
            throw new ArgumentException("processType");
         foreach( element in elements ) 
            action(element);
      }
   }

Was das Beispiel zeigt, ist eigentlich nur eine Art späte Bindung, dass ermöglicht eine eine von vielen möglichen Aktionen auf einer Folge von Elementen mit Nebenwirkungen aufrufen, ohne einen großen Schalter Konstrukt schreiben mit dem Wert zu entschlüsseln, die die definiert Aktion und es in sein entsprechendes Verfahren übersetzen.

Für VB.NET sollten Sie verwenden:

listVariable.ForEach(Sub(i) i.Property = "Value")

fließend man halten möchte, kann einen solchen Trick verwenden:

GetItems()
    .Select(i => new Action(i.DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();

MoreLinq hat IEnumerable<T>.ForEach und eine Tonne andere nützliche Erweiterungen. Es ist wahrscheinlich nicht wert, die Abhängigkeit nur für ForEach nehmen, aber es gibt eine Menge nützlicher Sachen drin.

https://www.nuget.org/packages/morelinq/

https://github.com/morelinq/MoreLINQ

Noch ein weiteres Beispiel ForEach

public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
    var workingAddresses = new List<AddressEntry>();

    addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));

    return workingAddresses;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top