Secrets of foreach in C#: How to Get the Current Iteration Index

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why doesn't foreach provide an index "out of the box"? And what to do about it?

\n\n

Have you ever written a foreach loop in C# and suddenly realized: "I need the number of the current element!" Your hand reaches for the keyboard to write for (int i = 0; i < list.Count; i++)... Stop! What if the collection is an IEnumerable that doesn't have an indexer? Or you're working with a HashSet? The good old for loop falls short here.

\n\n

This question is a real hit on Stack Overflow (millions of views!). And for good reason. foreach is the king of iterating collections: it's concise, safe, and works with any enumerable type. But its curse is the lack of a built-in counter. Today we'll fix that. I'll show you 4 proven ways to get the index in foreach, break down their pros, cons, and performance. Let's go!

\n\n

Method #1. The Classic: External Counter

\n\n

The simplest and most intuitive method. Declare a counter variable before the loop and increment it inside the body.

\n\n
List<string> fruits = new List<string>() { "Apple", "Banana", "Cherry", "Melon" };\nint index = 0;\nforeach (var fruit in fruits)\n{\n    Console.WriteLine($"Index {index}: {fruit}");\n    index++;\n}
\n\n

Pros:

\n
    \n
  • Extremely transparent. The code reads in a second.
  • \n
  • Requires no additional libraries or LINQ.
  • \n
  • Works with any IEnumerable.
  • \n
\n\n

Cons:

\n
    \n
  • You must remember to declare and increment the counter.
  • \n
  • If there's a continue inside the loop, the counter still increments (is that what you want?).
  • \n
  • With nested loops, it's easy to get confused about which index belongs to what.
  • \n
\n\n

This method is like a Swiss Army knife: reliable but basic. For 80% of tasks, it's more than enough.

\n\n

Method #2. LINQ Select with Index: Elegance in One Line

\n\n

If you love functional style, LINQ comes to the rescue. The Select() method has an overload that passes the element's index.

\n\n
List<string> fruits = new List<string>() { "Apple", "Banana", "Cherry", "Melon" };\n\nforeach (var (fruit, index) in fruits.Select((item, i) => (item, i)))\n{\n    Console.WriteLine($"Index {index}: {fruit}");\n}
\n\n

Notice the tuple deconstruction right in the loop header. This is one of the coolest features of modern C# (starting from version 7.0).

\n\n

Pros:

\n
    \n
  • The code becomes declarative: "take the collection, add its index to each element".
  • \n
  • The counter doesn't leak out — it's encapsulated inside LINQ.
  • \n
  • Works great with chains of other LINQ methods.
  • \n
\n\n

Cons:

\n
    \n
  • Slight overhead from creating tuples. For collections up to 10,000 elements, this is negligible, but for huge lists it can be critical.
  • \n
  • Requires importing System.Linq.
  • \n
\n\n

This method is the choice of those who write "clean" code. It's especially good when the index is needed not for loop control, but to pass to another method.

\n\n

Method #3. Indexer via ElementAt(): When the Collection is IEnumerable

\n\n

Sometimes you only have an IEnumerable, without an indexer, but you really want to get an element by its number. Technically, you could do this:

\n\n
IEnumerable<string> fruits = new List<string>() { "Apple", "Banana", "Cherry", "Melon" };\n\nfor (int i = 0; i < fruits.Count(); i++) // Warning: this is bad code!\n{\n    var fruit = fruits.ElementAt(i);\n    Console.WriteLine($"Index {i}: {fruit}");\n}
\n\n

Stop! Never do this. Count() and ElementAt() for IEnumerable each work in O(n). Your loop will become O(n²) — quadratic complexity. On 1000

Blogs

Book Recommendations