2010-09-17 6 views
9

Actualmente estoy trabajando con algunas bibliotecas que aplican la ejecución diferida a través de iteradores. En algunas situaciones, tengo la necesidad de "reenviar" el iterador recibido simplemente. Es decir. Tengo que obtener la instancia IEnumerable<T> del método llamado y devolverlo inmediatamente.Cuál es la diferencia exacta entre devolver una instancia de IEnumerable y la declaración de rendimiento en C#

Ahora mi pregunta: ¿Hay alguna diferencia relevante entre simplemente devolver el IEnumerable<T> recibido o volver a cederlo a través de un bucle?

IEnumerable<int> GetByReturn() 
{ 
    return GetIterator(); // GetIterator() returns IEnumerable<int> 
} 
// or: 
IEnumerable<int> GetByReYielding() 
{ 
    for(var item in GetIterator()) // GetIterator() returns IEnumerable<int> 
    { 
     yield return item; 
    } 
} 
+2

¿Cuál es la ventaja de volver a producir? Si desea realizar alguna acción en los artículos, no se moverá, ¿no? Si no, ¿por qué no utilizar la versión de código más corto? –

+0

Concluyo de mis experimentos (y de la inspección IL), que la ejecución de GetByReYielding() se aplazará por sí misma (independientemente de la forma en que funciona GetIterator()), pero la ejecución de GetByReturn() no. Eso es todo. – Nico

Respuesta

1

No hay ninguna diferencia relevante (aparte de tal vez rendimiento) entre los dos, ya que no está haciendo nada con el enumerador de GetIterator(). Tendría sentido volver a ceder si hiciera algo con el enumerador, como filtrarlo.

1

No veo que haya ninguna diferencia relevante que no sea el código de hinchazón.

3

son diferentes. Por ejemplo, si el GetIterator() declarado:

IEnumerable<int> GetIterator() { 
    List<int> result = new List<int>(); 
    for(int i = 0; i < 1000; i++) { 
     result.Add(i); 
    } 
    return result; 
} 

Si no es necesario volver a ceder, el GetIterator() y el bucle consiguió ejecuta inmediatamente. Por lo tanto, la respuesta depende de cómo implemente GetIterator(). Si es seguro que GetIterator() cederá, entonces no tiene sentido volver a producirlo.

+2

Incluso si usa un iterador, seguirá recorriendo toda la lista antes del primer _yield_. –

0

Hay una diferencia relevante.

La ejecución de GetByReYielding() se ejecutará de forma diferida (ya que es un bloque iterador). Si usó un parámetro en GetByReturn() o GetByReYielding() y verificó ese parámetro en el tiempo de ejecución en busca de nulidad (o hizo alguna otra validación), esta verificación se realizará inmediatamente cuando se invoque GetByReturn() pero no inmediatamente cuando se llame a GetByReYielding() ! La validación en GetByReYielding() se realizará de forma diferida, cuando el resultado se itera. - Esto es a menudo, bueno, "demasiado tarde". Ver aquí:

// Checks parameters early. - Fine. The passed argument will be checked directly when 
// GetByReturn() is called. 
IEnumerable<int> GetByReturn(IEnumerable<int> sequence) 
{ 
    if(null == sequence) 
    { 
     throw new ArgumentNullException("sequence"); 
    } 

    return GetIterator(); 
} 
// Checks parameters in a deferred manner. - Possibly not desired, it's "too" late. I.e.     // when the  
// result is iterated somewhere in a completely different location in your code the 
// argument passed once will be checked. 
IEnumerable<int> GetByReYielding(IEnumerable<int> sequence) 
{ 
    if(null == sequence) 
    { 
     throw new ArgumentNullException("sequence"); 
    } 

    for(var item in GetIterator()) 
    { 
     yield return item; 
    } 
} 

Sr. Skeet explicó este concepto en http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx. Los operadores de consulta estándar proporcionados en .Net usan funciones de derivador no diferidas (por ejemplo, Where()) que verifican los parámetros y luego llaman a la función del iterador principal (como mostré en mi implementación de GetByReturn()).

Espero que esto ayude.

Cuestiones relacionadas