2009-01-30 10 views

Respuesta

183

Si puede utilizar LINQ puede utilizar:

var e = enumerable.First(); 

Esto lanzará una excepción, aunque si numerable está vacía: en cuyo caso se puede utilizar:

var e = enumerable.FirstOrDefault(); 

FirstOrDefault() volverá default(T) si el enumerable está vacío, que será null para los tipos de referencia o el 'valor cero' predeterminado para los tipos de valor.

Si no puede utilizar LINQ, a continuación, su enfoque es técnicamente correcto y no es diferente de la creación de un enumerador usando los métodos GetEnumerator y MoveNext para recuperar el primer resultado (este ejemplo asume numerable es un IEnumerable<Elem>):

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

Joel Coehoorn mencionado .Single() en los comentarios; esto también funcionará, si está esperando que su enumerable contenga exactamente un elemento, sin embargo arrojará una excepción si está vacío o es más grande que un elemento. Existe un método correspondiente SingleOrDefault() que cubre este escenario de manera similar al FirstOrDefault(). Sin embargo, David B explica que SingleOrDefault() aún puede arrojar una excepción en el caso en que el enumerable contiene más de un elemento.

Edit: Gracias Marc Gravell por señalar que necesito para disponer de mi objeto IEnumerator después de usarlo - He editado el ejemplo no LINQ para mostrar la palabra clave using para implementar este patrón.

+1

usted debería ser 'using' enumer - pero aparte de eso, buena ;-P –

+1

Gracias Marc - editado. =) –

+2

Vale la pena señalar que SingleOrDefault devolverá 1) El único elemento si solo hay un artículo. 2) nulo si no hay elementos. 3) lanzar una excepción si hay más de un elemento. –

6

FirstOrDefault?

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

Bueno, usted no especificó qué versión de .Net está usando.

Asumiendo que tiene 3.5, otra forma es el método ElementAt:

var e = enumerable.ElementAt(0); 
+1

ElementAt (0), bonito y simple. – JMD

31

Sólo en caso de que estés utilizando .NET 2.0 y no tiene acceso a LINQ:

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

Este debe hacer lo que está buscando ... usa genéricos para que pueda obtener el primer elemento de cualquier tipo IEnumerable.

de llamadas que de este 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); 

Usted puede modificarlo con bastante facilidad para imitar .NET de 3,5 IEnumerable.ElementAt() método de extensión:

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

Llamarlo así:

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

Por supuesto, si hacer tener acceso a LINQ, entonces hay un montón de buenas respuestas publicadas ya ...

+0

Deberías estar 'usando' iter –

+0

Vaya, por supuesto tienes razón, gracias. He corregido mis ejemplos. – BenAlabaster

+0

¿Iteradores son desechables? No sabía que ... –

0

Use FirstOrDefault o un bucle foreach como se mencionó anteriormente. Se debe evitar traer manualmente un enumerador y llamar a Current. foreach dispondrá su enumerador por usted si implementa IDisposable. Cuando llame a MoveNext y Current, debe desecharlo manualmente (si corresponde).

+1

¿Qué evidencia hay de que se debe evitar al enumerador? Las pruebas de rendimiento en mi máquina indican que tiene una ganancia de rendimiento aproximada del 10% sobre Foreach. – BenAlabaster

+0

Depende de lo que enumera. Un erumerator podría cerrar la conexión a la base de datos, cerrar y cerrar el identificador de archivo, liberar algunos objetos bloqueados, etc. No descartar un enumerador de una lista de enteros no será dañino, supongo. – Mouk

0

Si su IEnumerable no expone que es <T> y LINQ falla, se puede escribir un método que utiliza la reflexión:

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

probar esto

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

también puedes probar la versión más genérico que le da el i-ésimo elemento

enumerable.ElementAtOrDefault (i));

creo que sirve

Cuestiones relacionadas