¿En qué orden un C # para cada bucle itera sobre un List <T>?

82

Me preguntaba sobre el orden en que un bucle foreach en C # recorre un System.Collections.Generic.List<T>objeto.

Encontré otra pregunta sobre el mismo tema, pero no siento que responda a mi pregunta a mi satisfacción.

Alguien dice que no se define ningún orden. Pero como dice otra persona, el orden en que atraviesa una matriz es fijo (de 0 a Longitud-1). 8.8.4 La declaración foreach

También se dijo que lo mismo es válido para cualquier clase estándar con un orden (por ejemplo List<T>). No puedo encontrar ninguna documentación que respalde eso. Entonces, por lo que sé, podría funcionar así ahora, pero tal vez en la próxima versión de .NET sea diferente (aunque podría ser poco probable).

También he mirado la List(t).Enumeratordocumentación sin suerte.

Otra pregunta relacionada establece que para Java, se menciona específicamente en la documentación:

List.iterator()devuelve un iterador sobre los elementos de esta lista en la secuencia adecuada. "

Estoy buscando algo así en la documentación de C #.

Gracias por adelantado.

Editar: Gracias a todos por todas sus respuestas (es increíble lo rápido que obtuve tantas respuestas). Lo que entiendo de todas las respuestas es que List<T>siempre itera en el orden de su indexación. Pero todavía me gustaría ver una documentación clara que indique esto, similar a la documentación de Java enList .

Matthijs Wessels
fuente

Respuestas:

13

En la página Fuente de referencia de Microsoft para List<T>Enumerator, se indica explícitamente que la iteración se realiza de 0 a Length-1:

internal Enumerator(List<T> list) {
    this.list = list;
    index = 0;
    version = list._version;
    current = default(T);
}

public bool MoveNext() {

    List<T> localList = list;

    if (version == localList._version && ((uint)index < (uint)localList._size)) 
    {                                                     
        current = localList._items[index];                    
        index++;
        return true;
    }
    return MoveNextRare();
}

Espero que siga siendo relevante para alguien

apokrovskyi
fuente
102

Básicamente depende de la IEnumeratoraplicación - pero para una List<T>siempre va a ir en el orden natural de la lista, es decir, el mismo orden que el indexador: list[0], list[1], list[2]etc.

No creo que esté documentado explícitamente, al menos, no he encontrado dicha documentación, pero creo que puede tratarlo como garantizado. Cualquier cambio en ese orden rompería inútilmente todo tipo de código. De hecho, me sorprendería ver alguna implementación IList<T>que desobedeciera esto. Es cierto que sería bueno verlo documentado específicamente ...

Jon Skeet
fuente
Literalmente, busqué una respuesta hace 5 segundos y estaba a punto de publicar algo similar. ¡Eres demasiado rápido!
Michael G
Gracias por tus respuestas. ¿Existe alguna documentación que lo garantice?
Matthijs Wessels
1
@Michael G: Sin embargo, no confiaría en el código de ejemplo de MSDN como documentación. He visto demasiados errores espantosos en él.
Jon Skeet
1
¿Alguien puede proporcionar la misma respuesta con respecto a la matriz?
yazanpro
12
@yazanpro: foreachdefinitivamente iterará en orden con una matriz.
Jon Skeet
8

En su enlace, la respuesta aceptada indica en la Especificación del lenguaje C # Versión 3.0, página 240 :

El orden en el que foreach atraviesa los elementos de una matriz es el siguiente: Para matrices unidimensionales, los elementos se atraviesan en orden de índice creciente, comenzando con el índice 0 y terminando con el índice Longitud - 1. Para matrices multidimensionales, los elementos se atraviesan de modo que los índices de la dimensión más a la derecha se incrementen primero, luego la siguiente dimensión izquierda, y así sucesivamente a la izquierda. El siguiente ejemplo imprime cada valor en una matriz bidimensional, en orden de elementos:

using System;
class Test
{
  static void Main() {
      double[,] values = {
          {1.2, 2.3, 3.4, 4.5},
          {5.6, 6.7, 7.8, 8.9}
      };
      foreach (double elementValue in values)
          Console.Write("{0} ", elementValue);
      Console.WriteLine();
  }
}

La salida producida es la siguiente: 1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9 En el ejemplo

int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers) Console.WriteLine(n);
the type of n is inferred to be int, the element type of numbers.
RvdK
fuente
2
Sí, pero esto es para una matriz. ¿También se mantiene automáticamente para la clase List <T>?
Matthijs Wessels
2
List <T> usa una matriz para su almacén de respaldo. Entonces sí.
Joel Mueller
9
Pero ese es un detalle de implementación. No se requiere List <T> para usar una matriz como su almacén de respaldo.
Eric Lippert
3
Me gustaría señalar la documentación para List <T> (nto el código de ejemplo): "La clase List <(Of <(T>)>) es el equivalente genérico de la clase ArrayList. Implementa el IList <(Of <(T>)>) interfaz genérica que utiliza una matriz cuyo tamaño se aumenta dinámicamente según sea necesario ". (énfasis mío)
RCIX
Hmm, supongo que siempre usa una matriz. Pero aún así, ¿eso significa que no puede devolver un enumerador invertido?
Matthijs Wessels
4

El orden lo define el iterador que se utiliza para recorrer una colección de datos utilizando un bucle foreach.

Si está utilizando una colección estándar que es indexable (como una Lista), atravesará la colección comenzando con el índice 0 y subiendo.

Si necesita controlar el orden, puede controlar cómo se maneja la iteración de la colección implementando su propio IEnumerable , o puede ordenar la lista de la manera que desee antes de ejecutar el bucle foreach.

Esto explica cómo funciona Enumerator para List genérico. Al principio, el elemento actual no está definido y usa MoveNext para ir al siguiente elemento.

Si lee MoveNext indica que comenzará con el primer elemento de la colección y de allí pasará al siguiente hasta llegar al final de la colección.

Brendan Enrick
fuente
Gracias por la respuesta y la adición (todos responden tan rápido que apenas puedo seguir el ritmo). Yo también leí eso. Tal vez solo estoy siendo una <palabra para especificar a alguien que quiere ser demasiado preciso>, pero siento que cuando dicen "primer elemento" se refieren al primer elemento que se va a iterar, no al elemento que es el primero de acuerdo según el orden de la clase iterada.
Matthijs Wessels
Bueno, si es tan importante que esté seguro de que va de la manera que espera, la mejor manera es hacer lo que dije e implementar IEnumerable usted mismo.
Brendan Enrick
1

Las listas parecen devolver los artículos en el orden en que se encuentran en la tienda de respaldo, por lo que si se agregan a la lista de esa manera, se devolverán de esa manera.

Si su programa depende del orden, es posible que desee ordenarlo antes de recorrer la lista.

Es algo tonto para las búsquedas lineales, pero si necesita el orden de cierta manera, lo mejor que puede hacer es hacer los elementos en ese orden.

Broam
fuente
1

Solo tuve que hacer algo similar a un truco rápido de código, aunque no funcionó para lo que estaba tratando de hacer, reordenó la lista por mí.

Usando LINQ para cambiar el orden

         DataGridViewColumn[] gridColumns = new DataGridViewColumn[dataGridView1.Columns.Count];
         dataGridView1.Columns.CopyTo(gridColumns, 0); //This created a list of columns

         gridColumns = (from n in gridColumns
                        orderby n.DisplayIndex descending
                        select n).ToArray(); //This then changed the order based on the displayindex
Aguafiestas
fuente
No veo cómo esto se relaciona con la pregunta. Tu código ni siquiera usa un List. Creo que no entendiste lo que quise decir con "lista".
Matthijs Wessels
Además, ¿por qué está copiando el contenido de Columnsa gridColumnsprimero? Creo que también podrías hacer:DataGridViewColumn[] gridColumns = dataGridView1.Columns.OrderByDescending(n => n.DisplayIndex).ToArray();
Matthijs Wessels
@MatthijsWessels No sería difícil cambiar eso para usar List, si quisiera.
Austin Henley
@AustinHenley Podrías hacer un foreach en IOrderedEnumerable, pero de eso no se trata la pregunta. También puede hacer ToList en lugar de ToArray, pero luego solo tiene una List nuevamente y la pregunta de si un foreach recorrerá los elementos en el orden especificado aún no tiene respuesta.
Matthijs Wessels