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

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

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

当前回答

如果集合是列表,则可以使用list。IndexOf,如:

foreach (Object o in collection)
{
    // ...
    @collection.IndexOf(o)
}

其他回答

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

我能问一下,你为什么想知道?

看起来你最有可能做以下三件事中的一件:

1)从集合中获取对象,但在这种情况下,您已经拥有它。

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

3)根据对象在循环中的顺序设置它的属性…尽管您可以在将对象添加到集合时轻松地进行设置。

使用LINQ, c# 7和系统。ValueTuple NuGet包,你可以这样做:

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

您可以使用常规的foreach构造,并能够直接访问值和索引,而不是作为对象的成员,并将这两个字段仅保留在循环的作用域中。基于这些原因,我相信这是最好的解决方案,如果你能够使用c# 7和System.ValueTuple。

使用计数器变量并没有什么错。事实上,无论使用for、foreach while还是do,计数器变量都必须在某处声明并递增。

所以,如果你不确定你是否有一个适当索引的集合,请使用这个习语:

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

否则,如果你知道你的可索引集合是O(1)索引访问(它将是数组和可能List<T>(文档没有说),但不一定对其他类型(如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'
}

它应该永远不需要通过调用MoveNext()和询问Current来“手动”操作IEnumerator - foreach是为你省去了那个特别的麻烦…如果您需要跳过项目,只需在循环体中使用continue。

为了完整起见,根据你对索引的处理(上面的结构提供了足够的灵活性),你可以使用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));

我们使用上面的AsParallel(),因为现在已经是2014年了,我们希望充分利用这些多核来加快速度。此外,对于“顺序”LINQ,你只能在List<T>和Array…上获得ForEach()扩展方法。而且不清楚使用它是否比简单的foreach更好,因为您仍然在运行单线程的更难看的语法。

如果集合是列表,则可以使用list。IndexOf,如:

foreach (Object o in collection)
{
    // ...
    @collection.IndexOf(o)
}

我对这个问题的解决方案是一个扩展方法WithIndex(),

http://code.google.com/p/ub-dotnet-utilities/source/browse/trunk/Src/Utilities/Extensions/EnumerableExtensions.cs

像这样使用它

var list = new List<int> { 1, 2, 3, 4, 5, 6 };    

var odd = list.WithIndex().Where(i => (i.Item & 1) == 1);
CollectionAssert.AreEqual(new[] { 0, 2, 4 }, odd.Select(i => i.Index));
CollectionAssert.AreEqual(new[] { 1, 3, 5 }, odd.Select(i => i.Item));