¿Cómo se obtiene el índice de la iteración actual de un bucle foreach?
¿Existe alguna construcción de lenguaje poco común que no haya encontrado (como las pocas que aprendí recientemente, algunas en Stack Overflow) en C# para obtener un valor que represente la iteración actual de un bucle foreach?
Por ejemplo, actualmente hago algo como esto dependiendo de las circunstancias:
int i = 0;
foreach (Object o in collection)
{
// ...
i++;
}
Ian Mercer publicó una solución similar a esta en el blog de Phil Haack :
foreach (var item in Model.Select((value, i) => new { i, value }))
{
var value = item.value;
var index = item.i;
}
Esto le proporciona el elemento ( item.value
) y su índice ( item.i
) mediante el uso de esta sobrecarga de LINQSelect
:
el segundo parámetro de la función [dentro de Seleccionar] representa el índice del elemento fuente.
Está new { i, value }
creando un nuevo objeto anónimo .
Las asignaciones de montón se pueden evitar ValueTuple
si usas C# 7.0 o posterior:
foreach (var item in Model.Select((value, i) => ( value, i )))
{
var value = item.value;
var index = item.i;
}
También puedes eliminarlo item.
mediante el uso de desestructuración automática:
foreach (var (value, i) in Model.Select((value, i) => ( value, i )))
{
// Access `value` and `i` directly here.
}
Es foreach
para iterar sobre colecciones que implementan IEnumerable
. Para ello, llama GetEnumerator
a la colección, que devolverá un archivo Enumerator
.
Este enumerador tiene un método y una propiedad:
MoveNext()
Current
Current
devuelve el objeto en el que se encuentra actualmente el enumerador y MoveNext
se actualiza Current
al siguiente objeto.
El concepto de índice es ajeno al concepto de enumeración y no se puede realizar.
Por eso, la mayoría de las colecciones se pueden recorrer utilizando un indexador y la construcción de bucle for.
Prefiero usar un bucle for en esta situación en comparación con el seguimiento del índice con una variable local.
Finalmente, C# 7.0 tiene una sintaxis decente para obtener un índice dentro de un foreach
bucle (es decir, tuplas):
foreach (var (item, index) in collection.WithIndex())
{
Debug.WriteLine($"{index}: {item}");
}
Se necesitaría un pequeño método de extensión:
using System.Collections.Generic;
public static class IEnumerableExtensions {
public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)
=> self.Select((item, index) => (item, index));
}