Question
In C#, is there a built-in way to get the current iteration index inside a foreach loop?
For example, I currently write code like this:
int i = 0;
foreach (object o in collection)
{
// use o and possibly i here
i++;
}
I want to know whether C# provides a language feature or common pattern for accessing the current index while iterating with foreach.
Short Answer
By the end of this page, you will understand why foreach in C# does not directly expose an index, how to track an index manually, when to switch to a for loop, and which real-world patterns developers use when they need both an item and its position.
Concept
foreach in C# is designed to iterate over items in a sequence, not over numeric positions.
When you write:
foreach (var item in collection)
{
// ...
}
you are saying:
- "Give me each item in this collection"
- not "Give me item number 0, then 1, then 2..."
That design makes foreach simple and readable. It works with many kinds of collections, including ones that may not even support indexing efficiently.
Why there is no built-in index in foreach
Not every enumerable sequence has a natural index.
For example:
List<T>has indexes- arrays have indexes
- a streamed sequence from LINQ may not have a meaningful index you can jump to
- custom
IEnumerable<T>types may only know how to return the next item
Because of that, the foreach statement focuses on values, not positions.
So how do you get the index?
In practice, you have three common options:
- Track the index manually
- Use a
forloop if the collection supports indexing
Mental Model
Think of foreach like reading items from a conveyor belt.
- With
foreach, you only receive the next item. - With a
forloop, you have a numbered shelf, and you can say "give me item at position 3".
So if your task is mainly "process every item," foreach is a great fit.
If your task is "process item number i," then for is often the better tool.
Manual indexing inside foreach is like keeping your own counter while watching the conveyor belt pass by.
Syntax and Examples
1. Manual index with foreach
int index = 0;
foreach (var item in collection)
{
Console.WriteLine($"Index: {index}, Value: {item}");
index++;
}
This is the simplest way to keep track of position while using foreach.
2. Use for with an indexable collection
If the collection is an array or List<T>, a for loop is often cleaner:
for (int i = 0; i < collection.Count; i++)
{
Console.WriteLine($"Index: {i}, Value: {collection[i]}");
}
Use this when:
- you need the index often
- you need to access neighboring items
- you need to modify items by position
3. LINQ with index
C# also supports getting indexes through LINQ:
using System.Linq;
( pair collection.Select((item, index) => { item, index }))
{
Console.WriteLine();
}
Step by Step Execution
Consider this code:
string[] names = { "Ana", "Ben", "Cara" };
int index = 0;
foreach (var name in names)
{
Console.WriteLine($"{index}: {name}");
index++;
}
Step-by-step
-
namesis created with three values:"Ana""Ben""Cara"
-
indexstarts at0. -
First loop iteration:
namebecomes"Ana"- output is
0: Ana index++changesindexfrom0to
Real World Use Cases
Numbered UI output
int i = 1;
foreach (var task in tasks)
{
Console.WriteLine($"{i}. {task.Title}");
i++;
}
Useful for menus, reports, and lists shown to users.
CSV or formatted output
You may need special formatting based on position, such as adding commas between items but not after the last one.
for (int i = 0; i < tags.Count; i++)
{
Console.Write(tags[i]);
if (i < tags.Count - 1)
{
Console.Write(", ");
}
}
Data import and validation
When validating rows from a file, an index helps identify the row number.
int rowNumber = 1;
foreach (var row in rows)
{
if (string.IsNullOrWhiteSpace(row.Email))
{
Console.WriteLine($"Row {rowNumber}: Email is required.");
}
rowNumber++;
}
Comparing with adjacent items
If you need previous or next values, a loop is usually better.
Real Codebase Usage
In real projects, developers usually choose between foreach and for based on intent.
Common patterns
1. Plain foreach for readability
foreach (var user in users)
{
SendWelcomeEmail(user);
}
This is the cleanest choice when position does not matter.
2. Manual counter when index is only extra information
int index = 0;
foreach (var file in files)
{
logger.LogInformation("Processing file {Index}: {Name}", index, file.Name);
ProcessFile(file);
index++;
}
This works well for logging, display numbering, or diagnostics.
3. for when position drives the logic
for (int i = 0; i < items.Count; i++)
{
if (i == 0)
{
continue;
}
items[i].PreviousId = items[i - ].Id;
}
Common Mistakes
1. Forgetting to increment the counter
Broken code:
int i = 0;
foreach (var item in collection)
{
Console.WriteLine(i);
}
Problem:
ialways stays0
Fix:
int i = 0;
foreach (var item in collection)
{
Console.WriteLine(i);
i++;
}
2. Incrementing in the wrong place
Broken code:
int i = 0;
foreach (var item in collection)
{
i++;
Console.WriteLine(i);
}
Problem:
- output starts at
1, not0
Fix depends on what you want:
- increment after using
ifor zero-based indexes
Comparisons
| Approach | Best when | Pros | Cons |
|---|---|---|---|
foreach | You only need each item | Clean, readable, works with any enumerable | No built-in index |
foreach + manual counter | You mostly need the item, but also want position | Simple, minimal change | Easy to forget index++ |
for | You need index-based logic | Direct access to i, good for neighbors and updates | Requires indexable collection |
LINQ Select((item, index) => ...) | You are transforming data and want index included | Expressive for projections | Less beginner-friendly, may be harder to read |
Cheat Sheet
Quick reference
foreach has no built-in index
foreach (var item in collection)
{
// item only
}
Manual index in foreach
int index = 0;
foreach (var item in collection)
{
// use item and index
index++;
}
for loop with index
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
}
LINQ index projection
var withIndexes = collection.Select((item, index) => new { item, index });
Rules of thumb
- Use
foreachwhen you only need values - Use
forwhen index is part of the logic
FAQ
Is there an index variable built into C# foreach?
No. The foreach statement gives you the current item, not its index.
What is the simplest way to get an index in foreach?
Use a separate counter variable:
int index = 0;
foreach (var item in collection)
{
index++;
}
Should I use for instead of foreach when I need the index?
Usually yes, especially if the collection supports indexing and your logic depends on position.
Can I get the index with LINQ in C#?
Yes. Select has an overload that provides both the item and the index.
Is foreach slower than for in C#?
Performance depends on the collection type and the scenario. In most beginner and everyday code, readability matters more than tiny differences.
Can I use foreach with collections that do not have indexes?
Yes. That is one of its main advantages. foreach works with any enumerable sequence.
Mini Project
Description
Build a small console program that prints a numbered to-do list. This project demonstrates when manual indexing in a foreach loop is useful and helps you practice the difference between zero-based indexing and user-friendly numbering.
Goal
Create a C# program that loops through a list of tasks and displays each task with its position number.
Requirements
[ "Create a list of at least five task names.", "Use a foreach loop to iterate through the tasks.", "Track the current position with a separate counter variable.", "Display task numbers starting from 1 instead of 0.", "Print each task in a readable format." ]
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
C# Type Checking Explained: typeof vs GetType() vs is
Learn when to use typeof, GetType(), and is in C#. Understand exact type checks, inheritance, and safe type testing clearly.
C# Version Numbers Explained: C# vs .NET Framework and Why “C# 3.5” Is Incorrect
Learn the correct C# version numbers, how they map to .NET releases, and why terms like C# 3.5 are inaccurate and confusing.