Come posso ottenere il primo elemento da un IEnumerable <T> in .net?


157

Voglio spesso afferrare il primo elemento di un file IEnumerable<T>in .net e non ho trovato un modo carino per farlo. Il migliore che ho trovato è:

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

Che schifo! Quindi, c'è un bel modo per farlo?

Risposte:


242

Se puoi usare LINQ puoi usare:

var e = enumerable.First();

Ciò genererà un'eccezione se enumerable è vuoto: nel qual caso è possibile utilizzare:

var e = enumerable.FirstOrDefault();

FirstOrDefault()restituirà default(T)se l'enumerabile è vuoto, che sarà nullper i tipi di riferimento o il "valore zero" predefinito per i tipi di valore.

Se non puoi usare LINQ, il tuo approccio è tecnicamente corretto e non è diverso dalla creazione di un enumeratore usando i metodi GetEnumeratore MoveNextper recuperare il primo risultato (questo esempio presuppone che enumerable sia un IEnumerable<Elem>):

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

Joel Coehoorn menzionato .Single()nei commenti; questo funzionerà anche se ti aspetti che il tuo enumerabile contenga esattamente un elemento, tuttavia genererà un'eccezione se è vuoto o più grande di un elemento. Esiste un SingleOrDefault()metodo corrispondente che copre questo scenario in modo simile a FirstOrDefault(). Tuttavia, David B spiega che SingleOrDefault()potrebbe ancora generare un'eccezione nel caso in cui l'enumerabile contenga più di un elemento.

Modifica: Grazie a Marc Gravell per aver sottolineato che devo smaltire il mio IEnumeratoroggetto dopo averlo usato - ho modificato l'esempio non LINQ per visualizzare la usingparola chiave per implementare questo modello.


2
Vale la pena sottolineare che SingleOrDefault restituirà 1) L'unico elemento se esiste un solo elemento. 2) null se non ci sono articoli. 3) genera un'eccezione se c'è più di un oggetto.
Amy B,

Buona chiamata David B - ha aggiunto una nota.
Erik Forbes,

36

Nel caso in cui si utilizzi .NET 2.0 e non si abbia accesso a LINQ:

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

Questo dovrebbe fare quello che stai cercando ... usa generici in modo da ottenere il primo elemento su qualsiasi tipo IEnumerable.

Chiamalo così:

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

O

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

È possibile modificarlo abbastanza rapidamente da imitare il metodo di estensione IEnumerable.ElementAt () di .NET 3.5:

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

Chiamandolo così:

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

Naturalmente se si fare avere accesso a LINQ, poi ci sono un sacco di buone risposte postato già ...


Oops, certo che hai ragione, grazie. Ho corretto i miei esempi.
BenAlabaster,

Se sei su 2.0, non hai neanche var.
ricorsivo il

1
Beh, immagino che se vuoi diventare pignolo, li dichiarerò correttamente: P
BenAlabaster,

Come qualcuno che è bloccato usando .NET 2.0, questo è stato molto apprezzato! Complimenti.
kayleeFrye_onDeck

26

Bene, non hai specificato quale versione di .Net stai usando.

Supponendo di avere 3.5, un altro modo è il metodo ElementAt:

var e = enumerable.ElementAt(0);

2
ElementAt (0), carino e semplice.
JMD


1

prova questo

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

0

Utilizzare FirstOrDefault o un ciclo foreach come già menzionato. È necessario evitare di recuperare manualmente un enumeratore e chiamare Current. foreach eliminerà il tuo enumeratore per te se implementa IDisposable. Quando si chiama MoveNext e Current, è necessario eliminarlo manualmente (se applicabile).


1
Quali prove ci sono che l'enumeratore dovrebbe essere evitato? I test delle prestazioni sulla mia macchina indicano che ha un guadagno approssimativo del 10% rispetto a ogni tentativo.
BenAlabaster,

Dipende da cosa stai enumerando. Un Erumeratore potrebbe chiudere la connessione al Database, eseguire loush e chiudere l'handle del file, rilasciare alcuni oggetti bloccati, ecc. Non eliminare un enumeratore di un elenco intero non sarà dannoso, immagino.
Mouk,

0

Se il tuo IEnumerable non lo <T>rivela e Linq fallisce, puoi scrivere un metodo usando reflection:

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

puoi anche provare la versione più generica che ti dà l'elemento ith

enumerable.ElementAtOrDefault (i));

spero che sia d'aiuto

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.