Is there some rare language construct I haven\'t encountered (like the few I\'ve learned recently, some on Stack Overflow) in C# to get a value representing the current iter
Literal Answer -- warning, performance may not be as good as just using an int
to track the index. At least it is better than using IndexOf
.
You just need to use the indexing overload of Select to wrap each item in the collection with an anonymous object that knows the index. This can be done against anything that implements IEnumerable.
System.Collections.IEnumerable collection = Enumerable.Range(100, 10);
foreach (var o in collection.OfType<object>().Select((x, i) => new {x, i}))
{
Console.WriteLine("{0} {1}", o.i, o.x);
}
Using @FlySwat's answer, I came up with this solution:
//var list = new List<int> { 1, 2, 3, 4, 5, 6 }; // Your sample collection
var listEnumerator = list.GetEnumerator(); // Get enumerator
for (var i = 0; listEnumerator.MoveNext() == true; i++)
{
int currentItem = listEnumerator.Current; // Get current item.
//Console.WriteLine("At index {0}, item is {1}", i, currentItem); // Do as you wish with i and currentItem
}
You get the enumerator using GetEnumerator
and then you loop using a for
loop. However, the trick is to make the loop's condition listEnumerator.MoveNext() == true
.
Since the MoveNext
method of an enumerator returns true if there is a next element and it can be accessed, making that the loop condition makes the loop stop when we run out of elements to iterate over.
This is how I do it, which is nice for its simplicity/brevity, but if you're doing a lot in the loop body obj.Value
, it is going to get old pretty fast.
foreach(var obj in collection.Select((item, index) => new { Index = index, Value = item }) {
string foo = string.Format("Something[{0}] = {1}", obj.Index, obj.Value);
...
}
Better to use keyword continue
safe construction like this
int i=-1;
foreach (Object o in collection)
{
++i;
//...
continue; //<--- safe to call, index will be increased
//...
}
Could do something like this:
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));
}
}
Ian Mercer posted a similar solution as this on Phil Haack's blog:
foreach (var item in Model.Select((value, i) => new { i, value }))
{
var value = item.value;
var index = item.i;
}
This gets you the item (item.value
) and its index (item.i
) by using this overload of LINQ's Select:
the second parameter of the function [inside Select] represents the index of the source element.
The new { i, value }
is creating a new anonymous object.
Heap allocations can be avoided by using ValueTuple
if you're using C# 7.0 or later:
foreach (var item in Model.Select((value, i) => ( value, i )))
{
var value = item.value;
var index = item.i;
}
You can also eliminate the item.
by using automatic destructuring:
<ol>
foreach ((MyType value, Int32 i) in Model.Select((value, i) => ( value, i )))
{
<li id="item_@i">@value</li>
}
</ol>