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

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

    It's only going to work for a List and not any IEnumerable, but in LINQ there's this:

    IList<Object> collection = new List<Object> { 
        new Object(), 
        new Object(), 
        new Object(), 
        };
    
    foreach (Object o in collection)
    {
        Console.WriteLine(collection.IndexOf(o));
    }
    
    Console.ReadLine();
    

    @Jonathan I didn't say it was a great answer, I just said it was just showing it was possible to do what he asked :)

    @Graphain I wouldn't expect it to be fast - I'm not entirely sure how it works, it could reiterate through the entire list each time to find a matching object, which would be a helluvalot of compares.

    That said, List might keep an index of each object along with the count.

    Jonathan seems to have a better idea, if he would elaborate?

    It would be better to just keep a count of where you're up to in the foreach though, simpler, and more adaptable.

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

    I wasn't sure what you were trying to do with the index information based on the question. However, in C#, you can usually adapt the IEnumerable.Select method to get the index out of whatever you want. For instance, I might use something like this for whether a value is odd or even.

    string[] names = { "one", "two", "three" };
    var oddOrEvenByName = names
        .Select((name, index) => new KeyValuePair<string, int>(name, index % 2))
        .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
    

    This would give you a dictionary by name of whether the item was odd (1) or even (0) in the list.

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

    I disagree with comments that a for loop is a better choice in most cases.

    foreach is a useful construct, and not replaceble by a for loop in all circumstances.

    For example, if you have a DataReader and loop through all records using a foreach it automatically calls the Dispose method and closes the reader (which can then close the connection automatically). This is therefore safer as it prevents connection leaks even if you forget to close the reader.

    (Sure it is good practise to always close readers but the compiler is not going to catch it if you don't - you can't guarantee you have closed all readers but you can make it more likely you won't leak connections by getting in the habit of using foreach.)

    There may be other examples of the implicit call of the Dispose method being useful.

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

    Unless your collection can return the index of the object via some method, the only way is to use a counter like in your example.

    However, when working with indexes, the only reasonable answer to the problem is to use a for loop. Anything else introduces code complexity, not to mention time and space complexity.

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

    This answer: lobby the C# language team for direct language support.

    The leading answer states:

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

    While this is true of the current C# language version (2020), this is not a conceptual CLR/Language limit, it can be done.

    The Microsoft C# language development team could create a new C# language feature, by adding support for a new Interface IIndexedEnumerable

    foreach (var item in collection with var index)
    {
        Console.WriteLine("Iteration {0} has value {1}", index, item);
    }
    
    //or, building on @user1414213562's answer
    foreach (var (item, index) in collection)
    {
        Console.WriteLine("Iteration {0} has value {1}", index, item);
    }
    

    If foreach () is used and with var index is present, then the compiler expects the item collection to declare IIndexedEnumerable interface. If the interface is absent, the compiler can polyfill wrap the source with an IndexedEnumerable object, which adds in the code for tracking the index.

    interface IIndexedEnumerable<T> : IEnumerable<T>
    {
        //Not index, because sometimes source IEnumerables are transient
        public long IterationNumber { get; }
    }
    

    Later, the CLR can be updated to have internal index tracking, that is only used if with keyword is specified and the source doesn't directly implement IIndexedEnumerable

    Why:

    • Foreach looks nicer, and in business applications, foreach loops are rarely a performance bottleneck
    • Foreach can be more efficient on memory. Having a pipeline of functions instead of converting to new collections at each step. Who cares if it uses a few more CPU cycles when there are fewer CPU cache faults and fewer garbage collections?
    • Requiring the coder to add index-tracking code, spoils the beauty
    • It's quite easy to implement (please Microsoft) and is backward compatible

    While most people here are not Microsoft employees, this is a correct answer, you can lobby Microsoft to add such a feature. You could already build your own iterator with an extension function and use tuples, but Microsoft could sprinkle the syntactic sugar to avoid the extension function

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

    If the collection is a list, you can use List.IndexOf, as in:

    foreach (Object o in collection)
    {
        // ...
        @collection.IndexOf(o)
    }
    
    0 讨论(0)
提交回复
热议问题