web-dev-qa-db-de.com

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

Ich möchte oft das erste Element eines IEnumerable<T> in .net packen, und ich habe keinen schönen Weg gefunden. Das Beste was ich mir ausgedacht habe ist:

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

Yuck! Gibt es einen schönen Weg, dies zu tun?

132
TimK

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

var e = enumerable.First();

Dies führt jedoch zu einer Ausnahme, wenn Enumerable leer ist. In diesem Fall können Sie Folgendes verwenden:

var e = enumerable.FirstOrDefault();

FirstOrDefault() gibt default(T) zurück, wenn das Aufzählungszeichen leer ist. Dies ist null für Referenztypen oder der Standardwert 'Nullwert' für Werttypen.

Wenn Sie LINQ nicht verwenden können, ist Ihr Ansatz technisch korrekt und unterscheidet sich nicht vom Erstellen eines Enumerators mithilfe der Methoden GetEnumerator und MoveNext, um das erste Ergebnis abzurufen (in diesem Beispiel wird angenommen, dass es sich bei der Aufzählung um einen IEnumerable<Elem> handelt)

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

Joel Coehoorn erwähnte .Single() in den Kommentaren; Dies funktioniert auch, wenn Sie erwarten, dass Ihr Enumerable genau ein Element enthält. Es wird jedoch eine Ausnahme ausgelöst, wenn es leer ist oder mehr als ein Element enthält. Es gibt eine entsprechende SingleOrDefault()-Methode, die dieses Szenario ähnlich wie FirstOrDefault() abdeckt. David B erklärt jedoch, dass SingleOrDefault() immer noch eine Ausnahme auslösen kann, wenn die Aufzählung mehr als ein Element enthält.

Edit: Danke Marc Gravell für den Hinweis, dass ich mein IEnumerator-Objekt nach der Verwendung entsorgen muss. Ich habe das Nicht-LINQ-Beispiel so bearbeitet, dass das Schlüsselwort using angezeigt wird, um dieses Muster zu implementieren.

210
Erik Forbes

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 verwendet Generika, damit Sie das erste Element eines beliebigen Typs IEnumerable erhalten.

Nenne 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 Erweiterungsmethode IEnumerable.ElementAt () 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;
    }
} 

Ich nenne es so:

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

Wenn Sie tun Zugang zu LINQ haben, gibt es natürlich schon viele gute Antworten ...

31
BenAlabaster

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

Angenommen, Sie haben 3.5, ist eine andere Methode die Methode ElementAt

var e = enumerable.ElementAt(0);
22
Adam Lassek

FirstOrDefault ?

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

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

0
Mouk

sie können auch die allgemeinere Version ausprobieren, die Ihnen das ith-Element gibt 

enumerable.ElementAtOrDefault (i));

ich hoffe es hilft

Wenn Ihr IEnumerable seinen <T> nicht enthüllt und Linq fehlschlägt, können Sie eine Methode unter Verwendung 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;
}
0
CZahrobsky

versuche dies

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