How do you get the index of the current iteration of a foreach loop?

后端 未结 30 1686
刺人心
刺人心 2020-11-22 07:05

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

相关标签:
30条回答
  • 2020-11-22 07:19

    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);
    }
    
    0 讨论(0)
  • 2020-11-22 07:20

    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.

    0 讨论(0)
  • 2020-11-22 07:21

    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);
        ...
    }
    
    0 讨论(0)
  • 2020-11-22 07:21

    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
        //...
    }
    
    0 讨论(0)
  • 2020-11-22 07:22

    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));
        }
    }
    
    0 讨论(0)
  • 2020-11-22 07:24

    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>
    
    0 讨论(0)
提交回复
热议问题