2009-01-30 15 views

risposta

183

Se è possibile utilizzare LINQ è possibile utilizzare:

var e = enumerable.First(); 

Questo sarà un'eccezione ma se enumerabile è vuota: in questo caso è possibile utilizzare:

var e = enumerable.FirstOrDefault(); 

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

Se non è possibile utilizzare LINQ, allora il vostro approccio è tecnicamente corretto e non è diverso che creare un enumeratore utilizzando i GetEnumerator e MoveNext metodi per recuperare il primo risultato (questo esempio presuppone enumerabile è 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 la tua enumerabile contenga esattamente un elemento, tuttavia genererà un'eccezione se è vuota o più grande di un elemento. Esiste un metodo SingleOrDefault() corrispondente che copre questo scenario in modo simile a FirstOrDefault(). Tuttavia, David B spiega che SingleOrDefault() può ancora generare un'eccezione nel caso in cui l'enumerable contenga più di un elemento.

Edit: Grazie Marc Gravell per aver ricordato che ho bisogno di disporre della mia IEnumerator oggetto dopo averlo usato - Ho modificato l'esempio non LINQ per visualizzare la parola chiave using per implementare questo modello.

+1

si dovrebbe essere 'using' enumer - ma a parte questo, buona ;-p –

+1

Grazie Marc - a cura. =) –

+2

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

6

FirstOrDefault?

Elem e = enumerable.FirstOrDefault(); 
//do something with e 
16

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); 
+1

ElementAt (0), bello e semplice. – JMD

31

Nel caso in cui si sta utilizzando .NET 2.0 e non hanno 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 i generici in modo da ottenere il primo oggetto su qualsiasi tipo IEnumerable.

chiamata in questo modo:

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 modificare abbastanza facilmente per imitare .NET 3.5 di IEnumerable.ElementAt() metodo di estensione:

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

chiamata in questo modo:

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à ...

+0

Si dovrebbe essere 'using' iter –

+0

Oops, ovviamente hai ragione, grazie. Ho corretto i miei esempi. – BenAlabaster

+0

Gli iteratori sono usa e getta? Non sapevo che ... –

0

Utilizzare FirstOrDefault o un ciclo foreach come già accennato. Cercare manualmente un enumeratore e chiamare Current dovrebbe essere evitato. foreach disporrà il tuo enumeratore per te se implementa IDisposable. Quando si chiama MoveNext e Current, è necessario eliminarlo manualmente (se possibile).

+1

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

+0

Dipende da cosa stai enumerando. Un Erumerator potrebbe chiudere la connessione al Database, chiudere e chiudere l'handle del file, rilasciare alcuni oggetti bloccati, ecc. Non disporre un enumeratore di qualche elenco intero non sarà dannoso, suppongo. – Mouk

0

Se l'IEnumerable non espone è <T> e Linq non riesce, è possibile scrivere un metodo che utilizza riflessione:

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

provare questo

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

si può anche provare la versione più generica che fornisce l'elemento iodico

enumerable.ElementAtOrDefault (i));

Speranza che aiuta

Problemi correlati