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

后端 未结 30 1685
刺人心
刺人心 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:34

    You can write your loop like this:

    var s = "ABCDEFG";
    foreach (var item in s.GetEnumeratorWithIndex())
    {
        System.Console.WriteLine("Character: {0}, Position: {1}", item.Value, item.Index);
    }
    

    After adding the following struct and extension method.

    The struct and extension method encapsulate Enumerable.Select functionality.

    public struct ValueWithIndex<T>
    {
        public readonly T Value;
        public readonly int Index;
    
        public ValueWithIndex(T value, int index)
        {
            this.Value = value;
            this.Index = index;
        }
    
        public static ValueWithIndex<T> Create(T value, int index)
        {
            return new ValueWithIndex<T>(value, index);
        }
    }
    
    public static class ExtensionMethods
    {
        public static IEnumerable<ValueWithIndex<T>> GetEnumeratorWithIndex<T>(this IEnumerable<T> enumerable)
        {
            return enumerable.Select(ValueWithIndex<T>.Create);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 07:35

    There's nothing wrong with using a counter variable. In fact, whether you use for, foreach while or do, a counter variable must somewhere be declared and incremented.

    So use this idiom if you're not sure if you have a suitably-indexed collection:

    var i = 0;
    foreach (var e in collection) {
       // Do stuff with 'e' and 'i'
       i++;
    }
    

    Else use this one if you know that your indexable collection is O(1) for index access (which it will be for Array and probably for List<T> (the documentation doesn't say), but not necessarily for other types (such as LinkedList)):

    // Hope the JIT compiler optimises read of the 'Count' property!
    for (var i = 0; i < collection.Count; i++) {
       var e = collection[i];
       // Do stuff with 'e' and 'i'
    }
    

    It should never be necessary to 'manually' operate the IEnumerator by invoking MoveNext() and interrogating Current - foreach is saving you that particular bother ... if you need to skip items, just use a continue in the body of the loop.

    And just for completeness, depending on what you were doing with your index (the above constructs offer plenty of flexibility), you might use Parallel LINQ:

    // First, filter 'e' based on 'i',
    // then apply an action to remaining 'e'
    collection
        .AsParallel()
        .Where((e,i) => /* filter with e,i */)
        .ForAll(e => { /* use e, but don't modify it */ });
    
    // Using 'e' and 'i', produce a new collection,
    // where each element incorporates 'i'
    collection
        .AsParallel()
        .Select((e, i) => new MyWrapper(e, i));
    

    We use AsParallel() above, because it's 2014 already, and we want to make good use of those multiple cores to speed things up. Further, for 'sequential' LINQ, you only get a ForEach() extension method on List<T> and Array ... and it's not clear that using it is any better than doing a simple foreach, since you are still running single-threaded for uglier syntax.

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

    I don't believe there is a way to get the value of the current iteration of a foreach loop. Counting yourself, seems to be the best way.

    May I ask, why you would want to know?

    It seems that you would most likley be doing one of three things:

    1) Getting the object from the collection, but in this case you already have it.

    2) Counting the objects for later post processing...the collections have a Count property that you could make use of.

    3) Setting a property on the object based on its order in the loop...although you could easily be setting that when you added the object to the collection.

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

    Why foreach ?!

    The simplest way is using for instead of foreach if you are using List:

    for (int i = 0 ; i < myList.Count ; i++)
    {
        // Do something...
    }
    

    Or if you want use foreach:

    foreach (string m in myList)
    {
         // Do something...
    }
    

    You can use this to know the index of each loop:

    myList.indexOf(m)
    
    0 讨论(0)
  • 2020-11-22 07:38

    Just add your own index. Keep it simple.

    int i = 0;
    foreach (var item in Collection)
    {
        item.index = i;
        ++i;
    }
    
    0 讨论(0)
  • 2020-11-22 07:40

    Here's a solution I just came up with for this problem

    Original code:

    int index=0;
    foreach (var item in enumerable)
    {
        blah(item, index); // some code that depends on the index
        index++;
    }
    

    Updated code

    enumerable.ForEach((item, index) => blah(item, index));
    

    Extension Method:

        public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T, int> action)
        {
            var unit = new Unit(); // unit is a new type from the reactive framework (http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx) to represent a void, since in C# you can't return a void
            enumerable.Select((item, i) => 
                {
                    action(item, i);
                    return unit;
                }).ToList();
    
            return pSource;
        }
    
    0 讨论(0)
提交回复
热议问题