Secretos de foreach en C#: cómo obtener el índice de la iteración actual

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué foreach no proporciona el índice "de fábrica"? ¿Y qué hacer al respecto?

\n\n

¿Alguna vez has escrito un bucle foreach en C# y de repente te has dado cuenta: "¡Necesito el número del elemento actual!". Tu mano se acerca al teclado para escribir for (int i = 0; i < list.Count; i++)... ¡Alto! ¿Y si la colección es un IEnumerable que no tiene indexador? ¿O estás trabajando con un HashSet? Aquí el viejo y confiable for se queda corto.

\n\n

Esta pregunta es un verdadero éxito en Stack Overflow (¡millones de visitas!). Y con razón. foreach es el rey de la iteración de colecciones: es conciso, seguro y funciona con cualquier tipo enumerable. Pero su maldición es la falta de un contador incorporado. Hoy lo solucionaremos. Te mostraré 4 métodos probados para obtener el índice en foreach, analizaré sus ventajas, desventajas y rendimiento. ¡Allá vamos!

\n\n

Método n.º 1. El clásico: contador externo

\n\n

El método más simple e intuitivo. Creamos una variable contador antes del bucle y la incrementamos en el cuerpo.

\n\n
List<string> fruits = new List<string>() { "Manzana", "Plátano", "Cereza", "Melón" };\nint index = 0;\nforeach (var fruit in fruits)\n{\n    Console.WriteLine($"Índice {index}: {fruit}");\n    index++;\n}
\n\n

Ventajas:

\n
    \n
  • Extremadamente claro. El código se lee en un segundo.
  • \n
  • No requiere bibliotecas adicionales ni LINQ.
  • \n
  • Funciona con cualquier IEnumerable.
  • \n
\n\n

Desventajas:

\n
    \n
  • Hay que recordar declarar e incrementar el contador.
  • \n
  • Si dentro del bucle hay un continue, el contador igualmente se incrementará (¿es eso lo que quieres?).
  • \n
  • En bucles anidados es fácil confundirse sobre a qué índice se refiere cada uno.
  • \n
\n\n

Este método es como una navaja suiza: confiable, pero básico. Para el 80% de las tareas es más que suficiente.

\n\n

Método n.º 2. LINQ Select con índice: elegancia en una sola línea

\n\n

Si te gusta el estilo funcional, LINQ viene al rescate. El método Select() tiene una sobrecarga que pasa el índice del elemento.

\n\n
List<string> fruits = new List<string>() { "Manzana", "Plátano", "Cereza", "Melón" };\n\nforeach (var (fruit, index) in fruits.Select((item, i) => (item, i)))\n{\n    Console.WriteLine($"Índice {index}: {fruit}");\n}
\n\n

Observa la deconstrucción de tupla directamente en el encabezado del bucle. Esta es una de las características más elegantes del C# moderno (desde la versión 7.0).

\n\n

Ventajas:

\n
    \n
  • El código se vuelve declarativo: "toma la colección, añade a cada elemento su índice".
  • \n
  • El contador no se filtra hacia afuera, está encapsulado dentro de LINQ.
  • \n
  • Combina perfectamente con cadenas de otros métodos LINQ.
  • \n
\n\n

Desventajas:

\n
    \n
  • Un pequeño overhead por la creación de tuplas. Para colecciones de hasta 10,000 elementos es imperceptible, pero para listas gigantescas puede ser crítico.
  • \n
  • Requiere importar System.Linq.
  • \n
\n\n

Este método es la elección de quienes escriben código "limpio". Es especialmente bueno cuando el índice no es necesario para controlar el bucle, sino para pasarlo a otro método.

\n\n

Método n.º 3. Indexador mediante ElementAt(): cuando la colección es IEnumerable

\n\n

A veces solo tienes un IEnumerable, sin indexador, pero realmente quieres obtener un elemento por su número. Técnicamente se puede hacer así:

\n\n
IEnumerable<string> fruits = new List<string>() { "Manzana", "Plátano", "Cereza", "Melón" };\n\nfor (int i = 0; i < fruits.Count(); i++) // ¡Atención: esto es mal código!\n{\n    var fruit = fruits.ElementAt(i);\n    Console.WriteLine($"Índice {i}: {fruit}");\n}
\n\n

¡Alto! Nunca hagas esto. Count() y ElementAt() para IEnumerable funcionan en O(n) cada uno. Tu bucle se convertirá en O(n²) — complejidad cuadrática. En 1000

Blogs

Book Recommendations