19 Stimmen

foreach mit generischer Liste, Erkennung der ersten Iteration bei Verwendung des Werttyps

Wenn foreach Wenn ich eine allgemeine Liste durcharbeite, möchte ich oft etwas anderes für das erste Element in der Liste tun:

List<object> objs = new List<object>
{
    new Object(),
    new Object(),
    new Object(),
    new Object()
};

foreach (object o in objs)
{
    if (o == objs.First())
    {
        System.Diagnostics.Debug.WriteLine("First object - do something special");
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("object Do something else");
    }
}

Dies wird ausgegeben:

    First object - do something special
    object Do something else
    object Do something else
    object Do something else

Das ist alles schön und gut.

Wenn meine generische Liste jedoch vom Typ Wert ist, schlägt dieser Ansatz fehl.

List<int> ints = new List<int> { 0, 0, 0, 0 };
foreach (int i in ints)
{
    if (i == ints.First())
    {
        System.Diagnostics.Debug.WriteLine("First int - do something special");
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("int Do something else");
    }
}

Dies wird ausgegeben:

    First int - do something special
    First int - do something special
    First int - do something special
    First int - do something special

Ich weiß, dass ich dies umprogrammieren könnte, um eine boolean Flaggenvariable oder traditionelle for Schleife, aber ich frage mich, ob es eine Möglichkeit gibt, herauszufinden, ob eine foreach-Schleife auf der ersten Iteration seiner Schleifenbildung ist.

29voto

Marc Gravell Punkte 970173

Nun, man könnte es mit expliziter Iteration codieren:

using(var iter = ints.GetEnumerator()) {
  if(iter.MoveNext()) {
     // do "first" with iter.Current

     while(iter.MoveNext()) {
       // do something with the rest of the data with iter.Current
     }
  }
}

Die Option bool flag (mit foreach ) ist wahrscheinlich einfacher... das ist es, was ich (fast) immer mache!

Eine andere Möglichkeit wäre LINQ:

if(ints.Any()) {
  var first = ints.First();
  // do something with first
}

foreach(var item in ints.Skip(1)) {
  // do something with the rest of them
}

Der Nachteil des obigen Beispiels ist, dass es versucht, die Liste 3 Mal zu betrachten... da wir wissen, dass es eine Liste ist, ist das in Ordnung - aber wenn wir nur eine IEnumerable<T> wäre es sinnvoll, sie nur einmal zu wiederholen (da die Quelle möglicherweise nicht wieder lesbar ist).

0 Stimmen

Was ist die "Die bool-Flag-Option (mit foreach) ist wahrscheinlich einfacher, obwohl" Ich meine, wie würden Sie eine boolsche Flagge verwenden, um eine erste Iteration zu erkennen, vor allem, wenn Sie dies für eine Reihe von 3 oder mehr verschachtelten foreach-Schleifen tun müssen?

1 Stimmen

@denas bool first = true; auf der Ebene, die Sie für "first" wünschen, und setzen Sie es nach jedem Element auf false...

10voto

Jon Skeet Punkte 1325502

Vor einiger Zeit schrieb ich SmartEnumerable (Teil von MiscUtil), das Ihnen mitteilt, ob das aktuelle Element das erste oder letzte ist, sowie seinen Index. Das könnte Ihnen helfen... es ist Teil von MiscUtil, das Open Source ist - Sie können nur SmartEnumerable unter der gleichen Lizenz nehmen, natürlich.

Beispielcode (c'n'p von der Webseite):

using System;
using System.Collections.Generic;

using MiscUtil.Collections;

class Example
{
    static void Main(string[] args)
    {
        List<string> list = new List<string>();
        list.Add("a");
        list.Add("b");
        list.Add("c");
        list.Add("d");
        list.Add("e");

        foreach (SmartEnumerable<string>.Entry entry in
                 new SmartEnumerable<string>(list))
        {
            Console.WriteLine ("{0,-7} {1} ({2}) {3}",
                               entry.IsLast  ? "Last ->" : "",
                               entry.Value,
                               entry.Index,
                               entry.IsFirst ? "<- First" : "");
        }
    }
}

EDIT: Beachten Sie, dass es zwar mit Referenztypen mit eindeutigen Referenzen funktioniert, aber dennoch fehlschlägt, wenn Sie eine Liste eingeben, in der die erste Referenz an anderer Stelle in der Liste auftaucht.

6 Stimmen

Ein perfekter "var"-Kandidat, wenn ich je einen gesehen habe ;-p

0 Stimmen

Außerdem ist eine ForEach-Überladung erforderlich, die eine Action<int, bool, T> oder eine Action<FooState<T>> annimmt - d.h. list.ForEach((index, isLast, value) => {...});

1 Stimmen

Var: Unbedingt. Natürlich vor C# 3 geschrieben :) Und einverstanden mit der Action<int, bool, T> oder zumindest Action<int, T>. Mehr für das More LINQ Projekt...

7voto

Amy B Punkte 104656
foreach(int i in ints.Take(1))
{
 //do first thing
}

foreach(int i in ints.Skip(1))
{
  //do other things
}

0 Stimmen

Die übersprungenen Elemente werden weiterhin iteriert.

3voto

Daniel Schaffer Punkte 54690

Da Sie bereits LINQ verwenden, können Sie dies tun:

var list = new List<Int32>();

// do something with list.First();

foreach (var item in list.Skip(1))
{
    // do other stuff
}

1voto

GWLlosa Punkte 23282

Das Problem, das Sie haben, liegt daran, dass die Gleichheit für Werttypen auf dem tatsächlichen Wert und nicht auf dem Verweis selbst basiert. Insbesondere in Ihrem Beispiel, Ihre Liste ist alle Nullen, so seine Frage, wenn currentItem == firstItem, und da sie beide Null sind, ist dies immer wahr. Für diese Art von Sache, ich habe immer gewickelt mit einem booleschen Flag.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X