2010-02-12 10 views
32

Me falta algo simple aquí.Uso de IEnumerable sin el bucle foreach

tomar el código siguiente:

public IEnumerable<int> getInt(){ 
    for(int i = 0; i < 10; i++){ 
    yield return i; 
    } 
} 

puedo llamar a esto con:

foreach (int j in obj.getInt()){ 
    //do something with j 
} 

¿Cómo puedo usar el método getInt sin el bucle foreach:

IEnumerable<int> iter = obj.getInt(); 
// do something with iter ?? 

Gracias.

ediciones

Para los que preguntan por qué me gustaría esto. Estoy iteración de dos cosas:

IEnumerator<int> iter = obj.getInt().GetEnumerator(); 
foreach(object x in xs){ 
    if (x.someCondition) continue; 
    iter.MoveNext(); 
    int n = iter.current(); 
    x.someProp = n; 
    etc... 
} 
+0

¿Qué es exactamente estás tratando de hacer? El punto entero en 'IEnumerable ' es permitir la iteración, y un bucle 'foreach' es la forma más eficiente de hacerlo. –

+0

@ Zakwe, quiero iterar dos enumerables a la vez. El foreach se mueve a través del primero, si cumple una condición, necesito el siguiente int (solo un ejemplo, no realmente un int). – Mark

Respuesta

52

Usted puede obtener una referencia a la Enumerator, utilizando el método GetEnumerator, a continuación, puede utilizar el método MoveNext() para seguir adelante, y usar la propiedad Current para acceder a sus elementos:

var enumerator = getInt().GetEnumerator(); 
while(enumerator.MoveNext()) 
{ 
    int n = enumerator.Current; 
    Console.WriteLine(n); 
} 
+1

Derecha. Pero, ¿qué beneficio obtienes al usar esto en vez de foreach? –

+0

Le permite hacer un procesamiento especial en, digamos, los primeros elementos, o detenerse por completo después del procesamiento de tantos, etc. Básicamente le da un poco más de versatilidad. –

+3

@Anon: puedes hacer lo mismo con 'foreach'. –

3

¿Qué tal esto?

IEnumerator<int> iter = obj.getInt(); 
using(iter) { 
    while(iter.MoveNext()) { 
     DoSomethingWith(iter.Current) 
    } 
} 
+0

@itowlson, ¡eso era exactamente lo que me estaba perdiendo! – Mark

29

Mi consejo: no te metas con los enumeradores. Caracteriza tu problema como una serie de operaciones en secuencias. Escribir código para expresar esas operaciones. Deje que los operadores de secuencia se encarguen de administrar los enumeradores.

Así que vamos a ver si entiendo esto. Tienes dos secuencias Digamos {2, 3, 5, 7, 12} y {"rana", "sapo"}. La operación lógica que desea realizar es, por ejemplo, "pasar por la primera secuencia. Cada vez que encuentre un número divisible entre tres, obtenga el siguiente elemento en la segunda secuencia. Haga algo con el par resultante (número, anfibio)".

Fácil de hacer. En primer lugar, se filtra la primera secuencia:

var filtered = firstSequence.Where(x=>x%3 == 0); 

A continuación, comprimir la secuencia filtrada con la segunda secuencia:

var zipped = filtered.Zip(
      secondSequence, 
      (y, z)=> new {Number = x, Amphibian = y}); 

Y ahora se puede iterar sobre la secuencia de cremallera y hacer lo que quiera con los pares:

foreach(var pair in zipped) 
    Console.WriteLine("{0} : {1}", pair.Number, pair.Amphibian); 

Easy peasy, sin jugar con los enumeradores.

+0

@Eric, gracias, me gusta, esto es muy parecido a lo que necesito. Paso más tiempo programando en Python y es muy similar a cómo lo haría allí (necesito estudiar las capacidades de C#). Sin embargo, tengo curiosidad, ¿cuáles son las posibles dificultades de los enumeradores? – Mark

+5

@Mark: (1) la gente se olvida de eliminarlos. (2) los encuestadores enfatizan * cómo * funciona el código en lugar de * para qué es * - leer código con enumeradores es como mirar una descripción de un motor de perforación cuando lo que desea describir es el agujero resultante.Ciertamente, a veces son necesarios, pero preferiría expresar mis operaciones en un nivel de abstracción superior al que tengo que hacer un seguimiento explícito de múltiples "cursores" en múltiples secuencias. –

+3

@Eric, una advertencia para su enfoque es que si esto actúa sobre un gran volumen de datos en la memoria, puede haber una ganancia de rendimiento al iterar manualmente las dos listas al mismo tiempo ... Si se trata de una pequeña cantidad de datos o de rendimiento No es una preocupación, entonces elijo el código elegante (es decir, el tuyo) sobre el código esencial. –

0

Aunque las respuestas aceptadas son correctas, observe que IEnumerator.Current no está definido antes de la primera llamada a MoveNext().

Si está interactuando sobre una matriz secundaria, querrá algo como:

IEnumerable<Foo> Foo() { ... } 

int AssignValues(List<TakesFoo> data) { 
    var count = 0; 
    var foo = Foo().GetEnumerator(); 

    // Step into the first element of the array; 
    // doing this does not discard a value from the IEnumerator 
    if (foo.MoveNext()) { 
    foreach (var x in data) { 
     x.SetFoo(foo.Current); 
     count += 1; 
     if (!foo.MoveNext()) { 
     break; 
     } 
    } 

    // Return count of assigned values 
    return count; 
} 
2

usando bucle for:

for (var enumerator = getInt().GetEnumerator(); enumerator.MoveNext();) 
{ 
    Console.WriteLine(enumerator.Current); 
} 
+0

Debe disponer el enumerador al final – CharlesB

0

Es importante mencionar que el deber del bucle foreach es para eliminar el enumerador si la instancia implementa IDisposable. En otras palabras, foreach debe ser reemplazado con algo como:

var enumerator = enumerable.GetEnumerator(); 

try 
{ 
    while (enumerator.MoveNext()) 
    { 
     var item = enumerator.Current; 
     // do stuff 
    } 
} 
finally 
{ 
    var disposable = enumerator as IDisposable; 
    if (disposable != null) 
    { 
     disposable.Dispose(); 
    } 
}