2009-06-26 14 views
21

Estoy revisando el código en el reflector, pero todavía no he descubierto cómo puede enumerarse a través de una colección al revés?¿Cómo funciona IEnumerable <T> .Reverse?

Dado que no hay información de recuento, y la enumeración siempre comienza desde el "inicio" de la colección, ¿verdad?

¿Es un inconveniente en .NET Framework? ¿El costo es más alto que la enumeración regular?

+0

No hay IEnumerable . Método de ataque que he visto (¡y MSDN parece ser compatible con esto)! – Noldorin

+8

Eso es porque es un método de extensión: http://msdn.microsoft.com/en-us/library/bb358497.aspx –

+2

@Noldorin: Enumerable.Reverse es un método de extensión en IEunumerable

Respuesta

40

En resumen, amortigua todo y luego lo recorre hacia atrás. No es eficiente, pero tampoco lo es OrderBy desde esa perspectiva.

En LINQ-to-Objects, existen operaciones de almacenamiento en búfer (Reverse, OrderBy, GroupBy, etc.) y operaciones que no son de almacenamiento en búfer (Where, Take, Skip, etc.).


Como ejemplo de un no-buffering Reverse aplicación utilizando IList<T>, tenga en cuenta:

public static IEnumerable<T> Reverse<T>(this IList<T> list) { 
    for (int i = list.Count - 1; i >= 0; i--) { 
     yield return list[i]; 
    } 
} 

Tenga en cuenta que esto es todavía un poco susceptible a errores si mutar la lista, mientras que la iteración se ... entonces no haga eso ;-p

+0

Gracias Marc. Al almacenar en búfer, ¿quiere decir que copia toda la colección, como dice Levi? –

+3

Exactamente eso, sí. –

+0

Gracias Marc. ¿Por curiosidad también sabes si se puede hacer una mejor enumeración con una nueva interfaz? Siempre pensé que IEnumerable era bueno (y lo es), pero ¿uno podría diseñar uno que tuviera un mejor rendimiento también en estos casos? –

6

Funciona copiando el IEnumerable subyacente <T> a una matriz, luego enumerando sobre esa matriz hacia atrás. Si el subyacente IEnumerable <T> implementa ICollection <T> (como T [], List <T>, etc.), el paso de copiado se omite y el enumerador simplemente itera sobre la colección subyacente directamente.

Para obtener más información, echa un vistazo a System.Linq.Buffer <TElement> in Reflector.

Editar: La colección subyacente siempre se copia, incluso si se trata de un ICollection <TElement>. Esto evita que los cambios en la colección subyacente sean propagados por el Buffer <TElement>.

+0

Estoy buscando en el Buffer ctor, y no puedo ver ningún momento cuando se salte el paso de copia - cuidado para elaborar? –

+0

@Marc, @Levi: Todavía hace una copia, pero lo hace usando el método ICollection .CopyTo en lugar de enumerar la secuencia. – LukeH

3

carga todos los elementos en la memoria y luego los atraviesa (hacia atrás). esto es mucho menos eficiente.

-3

Editar: Opps, escribió la prueba incorrecta para el revés, mi disculpa por la respuesta incorrecta. Realiza un búfer después de la prueba de corrección (utilizando enumerable devuelto por Reverso())

Parece que el método de extensión inversa solo funciona cuando se llena la colección. Al usar el retorno de rendimiento, no hace nada.

Se ha producido un problema al utilizar el pensamiento inverso, debe amortiguar el funcionamiento para que funcione, y ha comprobado que no funciona con el rendimiento. Simplemente pasa y no haces nada. a continuación está mi código de prueba.

 [TestMethod] 
    public void loopTest() 
    { 
     var series = this.GetSeries(); 

     series.Reverse(); 

     foreach (var l in series) 
     { 
      Debug.WriteLine(l); 
     } 
    } 

    private IEnumerable<long> GetSeries() 
    { 
     var series = new List<long>() { 1, 2, 3, 4 }; 

     foreach (var entry in series) 
     { 
      Debug.WriteLine(entry); 

      yield return entry; 
     } 
    } 

inversa no llame a la función GetSeries en absoluto, todas las conversaciones de amortiguamiento en este foro se ve desde el aire.

+1

El método de extensión inversa no revierte la colección subyacente. Más bien, produce un nuevo enumerable que enumerará la colección en orden inverso. Su línea que se parece a esta 'serie.Reverse();' no tiene efecto. Si cambia la línea para que se vea así 'var reversed = series.Reverse();' y luego itere sobre 'reversed', entonces obtendrá la respuesta correcta. – wageoghe

+0

Gracias wageoghe, por señalar problema. – mamu

Cuestiones relacionadas