C# 如何获得 foreach 循环当前迭代的索引?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43021/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 09:52:25  来源:igfitidea点击:

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

提问by Matt Mitchell

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 iteration of a foreach loop?

在 C# 中是否有一些我没有遇到过的罕见语言构造(比如我最近学到的一些,一些在 Stack Overflow 上)来获取表示 foreach 循环当前迭代的值?

For instance, I currently do something like this depending on the circumstances:

例如,我目前根据情况做这样的事情:

int i = 0;
foreach (Object o in collection)
{
    // ...
    i++;
}

采纳答案by FlySwat

The foreachis for iterating over collections that implement IEnumerable. It does this by calling GetEnumeratoron the collection, which will return an Enumerator.

foreach是遍历实现集合IEnumerable。它通过调用GetEnumerator集合来完成此操作,该集合将返回一个Enumerator.

This Enumerator has a method and a property:

这个 Enumerator 有一个方法和一个属性:

  • MoveNext()
  • Current
  • 移动下一步()
  • 当前的

Currentreturns the object that Enumerator is currently on, MoveNextupdates Currentto the next object.

Current返回 Enumerator 当前所在的对象,MoveNext更新Current到下一个对象。

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.

因此,大多数集合都可以使用索引器和 for 循环构造进行遍历。

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

与使用局部变量跟踪索引相比,我更喜欢在这种情况下使用 for 循环。

回答by Joseph Daigle

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.

但是,在使用索引时,解决问题的唯一合理方法是使用 for 循环。其他任何事情都会引入代码复杂性,更不用说时间和空间复杂性了。

回答by bryansh

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.

我不相信有一种方法可以获取 foreach 循环的当前迭代值。数数自己,似乎是最好的方法。

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.

1) 从集合中获取对象,但在本例中您已经拥有它。

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

2) 为以后的后期处理计算对象...集合有一个可以使用的 Count 属性。

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.

3)根据对象在循环中的顺序在对象上设置属性......尽管在将对象添加到集合时可以轻松设置该属性。

回答by Brad Wilson

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));
    }
}

回答by crucible

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

它只适用于列表而不适用于任何 IEnumerable,但在 LINQ 中是这样的:

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 :)

@Jonathan我没有说这是一个很好的答案,我只是说这只是表明可以按照他的要求去做:)

@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.

@Graphain 我不希望它很快 - 我不完全确定它是如何工作的,它每次都可以通过整个列表重复以找到匹配的对象,这将是大量的比较。

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

也就是说,List 可能会保留每个对象的索引以及计数。

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.

最好只计算你在 foreach 中的位置,更简单,更适应性强。

回答by Amy B

Literal Answer -- warning, performance may not be as good as just using an intto track the index. At least it is better than using IndexOf.

字面答案——警告,性能可能不如仅使用 anint来跟踪索引。至少它比使用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.

您只需要使用 Select 的索引重载将集合中的每个项目都包装在一个知道索引的匿名对象中。这可以针对任何实现 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);
}

回答by mike nelson

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

我不同意for在大多数情况下循环是更好选择的评论。

foreachis a useful construct, and not replaceble by a forloop in all circumstances.

foreach是一个有用的构造,并且for在所有情况下都不能被循环替换。

For example, if you have a DataReaderand loop through all records using a foreachit automatically calls the Disposemethod 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.

例如,如果您有一个DataReader并使用 a 循环遍历所有记录,foreach它会自动调用Dispose方法并关闭读取器(然后可以自动关闭连接)。因此,这更安全,因为即使您忘记关闭阅读器,它也可以防止连接泄漏。

(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.)

(当然,始终关闭读取器是一种很好的做法,但如果不这样做,编译器将不会捕获它-您不能保证已关闭所有读取器,但是您可以通过获取更可能不会泄漏连接习惯使用foreach。)

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

可能还有其他Dispose有用的方法的隐式调用示例。

回答by mat3

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;
    }

回答by Sachin

int index;
foreach (Object o in collection)
{
    index = collection.indexOf(o);
}

This would work for collections supporting IList.

这适用于支持IList.

回答by Brian Gideon

You could wrap the original enumerator with another that does contain the index information.

您可以用另一个包含索引信息的枚举器包装原始枚举器。

foreach (var item in ForEachHelper.WithIndex(collection))
{
    Console.Write("Index=" + item.Index);
    Console.Write(";Value= " + item.Value);
    Console.Write(";IsLast=" + item.IsLast);
    Console.WriteLine();
}

Here is the code for the ForEachHelperclass.

这是ForEachHelper该类的代码。

public static class ForEachHelper
{
    public sealed class Item<T>
    {
        public int Index { get; set; }
        public T Value { get; set; }
        public bool IsLast { get; set; }
    }

    public static IEnumerable<Item<T>> WithIndex<T>(IEnumerable<T> enumerable)
    {
        Item<T> item = null;
        foreach (T value in enumerable)
        {
            Item<T> next = new Item<T>();
            next.Index = 0;
            next.Value = value;
            next.IsLast = false;
            if (item != null)
            {
                next.Index = item.Index + 1;
                yield return item;
            }
            item = next;
        }
        if (item != null)
        {
            item.IsLast = true;
            yield return item;
        }            
    }
}