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

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

    The foreach is for iterating over collections that implement IEnumerable. It does this by calling GetEnumerator on the collection, which will return an Enumerator.

    This Enumerator has a method and a property:

    • MoveNext()
    • Current

    Current returns the object that Enumerator is currently on, MoveNext updates Current to the next object.

    The concept of an index is foreign to the concept of enumeration, and cannot be done.

    Because of that, most collections are able to be traversed using an indexer and the for loop construct.

    I greatly prefer using a for loop in this situation compared to tracking the index with a local variable.

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

    C# 7 finally gives us an elegant way to do this:

    static class Extensions
    {
        public static IEnumerable<(int, T)> Enumerate<T>(
            this IEnumerable<T> input,
            int start = 0
        )
        {
            int i = start;
            foreach (var t in input)
            {
                yield return (i++, t);
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var s = new string[]
            {
                "Alpha",
                "Bravo",
                "Charlie",
                "Delta"
            };
    
            foreach (var (i, t) in s.Enumerate())
            {
                Console.WriteLine($"{i}: {t}");
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 07:17

    For interest, Phil Haack just wrote an example of this in the context of a Razor Templated Delegate (http://haacked.com/archive/2011/04/14/a-better-razor-foreach-loop.aspx)

    Effectively he writes an extension method which wraps the iteration in an "IteratedItem" class (see below) allowing access to the index as well as the element during iteration.

    public class IndexedItem<TModel> {
      public IndexedItem(int index, TModel item) {
        Index = index;
        Item = item;
      }
    
      public int Index { get; private set; }
      public TModel Item { get; private set; }
    }
    

    However, while this would be fine in a non-Razor environment if you are doing a single operation (i.e. one that could be provided as a lambda) it's not going to be a solid replacement of the for/foreach syntax in non-Razor contexts.

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

    I built this in LINQPad:

    var listOfNames = new List<string>(){"John","Steve","Anna","Chris"};
    
    var listCount = listOfNames.Count;
    
    var NamesWithCommas = string.Empty;
    
    foreach (var element in listOfNames)
    {
        NamesWithCommas += element;
        if(listOfNames.IndexOf(element) != listCount -1)
        {
            NamesWithCommas += ", ";
        }
    }
    
    NamesWithCommas.Dump();  //LINQPad method to write to console.
    

    You could also just use string.join:

    var joinResult = string.Join(",", listOfNames);
    
    0 讨论(0)
  • 2020-11-22 07:18

    Using LINQ, C# 7, and the System.ValueTuple NuGet package, you can do this:

    foreach (var (value, index) in collection.Select((v, i)=>(v, i))) {
        Console.WriteLine(value + " is at index " + index);
    }
    

    You can use the regular foreach construct and be able to access the value and index directly, not as a member of an object, and keeps both fields only in the scope of the loop. For these reasons, I believe this is the best solution if you are able to use C# 7 and System.ValueTuple.

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

    How about something like this? Note that myDelimitedString may be null if myEnumerable is empty.

    IEnumerator enumerator = myEnumerable.GetEnumerator();
    string myDelimitedString;
    string current = null;
    
    if( enumerator.MoveNext() )
        current = (string)enumerator.Current;
    
    while( null != current)
    {
        current = (string)enumerator.Current; }
    
        myDelimitedString += current;
    
        if( enumerator.MoveNext() )
            myDelimitedString += DELIMITER;
        else
            break;
    }
    
    0 讨论(0)
提交回复
热议问题