Wie erhalte ich das erste Element von einem IEnumerable <T> in .net?

155

Ich möchte oft das erste Element eines IEnumerable<T>in .net greifen , und ich habe keinen guten Weg gefunden, dies zu tun. Das Beste, was ich mir ausgedacht habe, ist:

foreach(Elem e in enumerable) {
  // do something with e
  break;
}

Yuck! Gibt es eine gute Möglichkeit, dies zu tun?

TimK
quelle

Antworten:

240

Wenn Sie LINQ verwenden können, können Sie Folgendes verwenden:

var e = enumerable.First();

Dies löst jedoch eine Ausnahme aus, wenn enumerable leer ist. In diesem Fall können Sie Folgendes verwenden:

var e = enumerable.FirstOrDefault();

FirstOrDefault()wird zurückgegeben, default(T)wenn die Aufzählung leer ist, was nullfür Referenztypen oder der Standardwert 'Nullwert' für Werttypen gilt.

Wenn Sie LINQ nicht verwenden können, ist Ihr Ansatz technisch korrekt und unterscheidet sich nicht vom Erstellen eines Enumerators mit den Methoden GetEnumeratorund MoveNextzum Abrufen des ersten Ergebnisses (in diesem Beispiel wird davon ausgegangen, dass Enumerable ein ist IEnumerable<Elem>):

Elem e = myDefault;
using (IEnumerator<Elem> enumer = enumerable.GetEnumerator()) {
    if (enumer.MoveNext()) e = enumer.Current;
}

Joel Coehoorn erwähnt .Single()in den Kommentaren; Dies funktioniert auch, wenn Sie erwarten, dass Ihre Aufzählung genau ein Element enthält. Es wird jedoch eine Ausnahme ausgelöst, wenn sie entweder leer oder größer als ein Element ist. Es gibt eine entsprechende SingleOrDefault()Methode, die dieses Szenario auf ähnliche Weise abdeckt wie FirstOrDefault(). Doch David B erklärt , dass SingleOrDefault()immer noch eine Ausnahme in dem Fall werfen kann , wenn die zählbaren mehr als ein Element enthält.

Bearbeiten: Vielen Dank an Marc Gravell, der darauf hingewiesen hat, dass ich mein IEnumeratorObjekt nach der Verwendung entsorgen muss. Ich habe das Nicht-LINQ-Beispiel bearbeitet, um das usingSchlüsselwort zur Implementierung dieses Musters anzuzeigen .

Erik Forbes
quelle
2
Es sei darauf hingewiesen, dass SingleOrDefault 1) das einzige Element zurückgibt, wenn es nur ein Element gibt. 2) null, wenn keine Elemente vorhanden sind. 3) eine Ausnahme auslösen, wenn mehr als ein Element vorhanden ist.
Amy B
Guter Anruf David B - fügte eine Notiz hinzu.
Erik Forbes
36

Nur für den Fall, dass Sie .NET 2.0 verwenden und keinen Zugriff auf LINQ haben:

 static T First<T>(IEnumerable<T> items)
 {
     using(IEnumerator<T> iter = items.GetEnumerator())
     {
         iter.MoveNext();
         return iter.Current;
     }
 }

Dies sollte das tun, wonach Sie suchen ... es werden Generika verwendet, damit Sie das erste Element für einen beliebigen IEnumerable-Typ erhalten.

Nennen Sie es so:

List<string> items = new List<string>() { "A", "B", "C", "D", "E" };
string firstItem = First<string>(items);

Oder

int[] items = new int[] { 1, 2, 3, 4, 5 };
int firstItem = First<int>(items);

Sie können es leicht genug ändern, um die IEnumerable.ElementAt () -Erweiterungsmethode von .NET 3.5 nachzuahmen:

static T ElementAt<T>(IEnumerable<T> items, int index)
{
    using(IEnumerator<T> iter = items.GetEnumerator())
    {
        for (int i = 0; i <= index; i++, iter.MoveNext()) ;
        return iter.Current;
    }
} 

Nennen wir es so:

int[] items = { 1, 2, 3, 4, 5 };
int elemIdx = 3;
int item = ElementAt<int>(items, elemIdx);

Natürlich , wenn Sie tun Zugang zu LINQ haben, dann gibt es viele gute Antworten bereits gebucht ...

BenAlabaster
quelle
Ups, natürlich hast du recht, danke. Ich habe meine Beispiele korrigiert.
BenAlabaster
Wenn Sie auf 2.0 sind, haben Sie auch keine var.
rekursiv
1
Nun, ich denke, wenn du pingelig werden willst, erkläre ich sie richtig: P
BenAlabaster
Als jemand, der mit .NET 2.0 nicht weiterkommt, wurde dies sehr geschätzt! Ein großes Lob.
KayleeFrye_onDeck
26

Nun, Sie haben nicht angegeben, welche Version von .Net Sie verwenden.

Angenommen, Sie haben 3.5, ist die ElementAt-Methode eine andere Möglichkeit:

var e = enumerable.ElementAt(0);
Adam Lassek
quelle
2
ElementAt (0), nett und einfach.
JMD
6

FirstOrDefault ?

Elem e = enumerable.FirstOrDefault();
//do something with e
Amy B.
quelle
1

Versuche dies

IEnumberable<string> aa;
string a = (from t in aa where t.Equals("") select t.Value).ToArray()[0];
daodao
quelle
0

Verwenden Sie FirstOrDefault oder eine foreach-Schleife, wie bereits erwähnt. Das manuelle Abrufen eines Enumerators und das Aufrufen von Current sollten vermieden werden. foreach entsorgt Ihren Enumerator für Sie, wenn er IDisposable implementiert. Wenn Sie MoveNext und Current aufrufen, müssen Sie es manuell entsorgen (falls zutreffend).

Mouk
quelle
1
Welche Beweise gibt es dafür, dass der Enumerator vermieden werden sollte? Leistungstests auf meinem Computer zeigen, dass der Leistungsgewinn gegenüber foreach um ca. 10% liegt.
BenAlabaster
Es hängt davon ab, worüber Sie aufzählen. Ein Erumerator könnte die Verbindung zur Datenbank schließen, das Dateihandle fushen und schließen, einige gesperrte Objekte freigeben usw. Es wird wohl nicht schädlich sein, einen Enumerator für eine Ganzzahlliste nicht zu entsorgen.
Mouk
0

Wenn Ihr IEnumerable es nicht verfügbar macht <T>und Linq fehlschlägt, können Sie eine Methode mithilfe von Reflection schreiben:

public static T GetEnumeratedItem<T>(Object items, int index) where T : class
{
  T item = null;
  if (items != null)
  {
    System.Reflection.MethodInfo mi = items.GetType()
      .GetMethod("GetEnumerator");
    if (mi != null)
    {
      object o = mi.Invoke(items, null);
      if (o != null)
      {
        System.Reflection.MethodInfo mn = o.GetType()
          .GetMethod("MoveNext");
        if (mn != null)
        {
          object next = mn.Invoke(o, null);
          while (next != null && next.ToString() == "True")
          {
            if (index < 1)
            {
              System.Reflection.PropertyInfo pi = o
                .GetType().GetProperty("Current");
              if (pi != null) item = pi
                .GetValue(o, null) as T;
              break;
            }
            index--;
          }
        }
      }
    }
  }
  return item;
}
CZahrobsky
quelle
0

Sie können auch die allgemeinere Version ausprobieren, die Ihnen das i-te Element gibt

enumerable.ElementAtOrDefault (i));

ich hoffe es hilft

zufällige Person im Internet
quelle