Problem
Is there a way to acquire a value indicating the current iteration of a foreach loop in C# that I haven’t discovered (like the handful I’ve learnt recently, some on Stack Overflow)?
For example, depending on the situation, I currently do something like this:
int i = 0;
foreach (Object o in collection)
{
// ...
i++;
}
Asked by Matt Mitchell
Solution #1
On Phil Haack’s site, Ian Mercer offered a similar solution:
foreach (var item in Model.Select((value, i) => new { i, value }))
{
var value = item.value;
var index = item.i;
}
This overload of LINQ’s Select: returns the item (item.value) and its index (item.i).
A new anonymous object is created by the new I value.
If you’re using C# 7.0 or later, you can prevent heap allocations by using ValueTuple:
foreach (var item in Model.Select((value, i) => ( value, i )))
{
var value = item.value;
var index = item.i;
}
You could also get rid of the thing. Using destructuring software that is automatically generated:
foreach (var (value, i) in Model.Select((value, i) => ( value, i )))
{
// Access `value` and `i` directly here.
}
Answered by bcahill
Solution #2
Iterating through collections that implement IEnumerable is done with foreach. It accomplishes this by retrieving an Enumerator from the collection using GetEnumerator.
There is a method and a property on this Enumerator:
MoveNext moves Current to the next object, whereas Current returns the object that Enumerator is currently on.
The idea of an index is incompatible with the idea of enumeration, hence it can’t be done.
As a result, most collections can be traversed with the use of an indexer and the for loop construct.
When opposed to tracking the index with a local variable, I much prefer using a for loop in this situation.
Answered by FlySwat
Solution #3
Finally, C#7 has a good syntax for getting an index (i.e. tuples) inside a foreach loop:
foreach (var (item, index) in collection.WithIndex())
{
Debug.WriteLine($"{index}: {item}");
}
A simple extension method is required:
public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)
=> self.Select((item, index) => (item, index));
Answered by user1414213562
Solution #4
Something along these lines could be done:
public static class ForEachExtensions
{
public static void ForEachWithIndex<T>(this IEnumerable<T> enumerable, Action<T, int> handler)
{
int idx = 0;
foreach (T item in enumerable)
handler(item, idx++);
}
}
public class Example
{
public static void Main()
{
string[] values = new[] { "foo", "bar", "baz" };
values.ForEachWithIndex((item, idx) => Console.WriteLine("{0}: {1}", idx, item));
}
}
Answered by Brad Wilson
Solution #5
In most circumstances, I disagree with remarks that a for loop is a preferable solution.
Foreach is a valuable construct that cannot always be replaced by a for loop.
For example, if you use a foreach loop to loop through all records in a DataReader, the Dispose function is immediately called and the reader is closed (which can then close the connection automatically). As a result, even if you neglect to close the reader, connection leaks are prevented.
(Sure, closing readers is good practice, but the compiler won’t catch it if you don’t – you can’t guarantee you’ve closed all readers, but getting into the habit of using foreach will make it more likely you won’t leak connections.)
There may be other instances where the Dispose method’s implicit call is useful.
Answered by mike nelson
Post is based on https://stackoverflow.com/questions/43021/how-do-you-get-the-index-of-the-current-iteration-of-a-foreach-loop