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

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

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

当前回答

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

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

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

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

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

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

其他回答

出于兴趣,Phil Haack刚刚在Razor Templated Delegate的上下文中写了一个例子(http://haacked.com/archive/2011/04/14/a-better-razor-foreach-loop.aspx)。

实际上,他编写了一个扩展方法,将迭代包装在一个“IteratedItem”类中(见下文),允许在迭代期间访问索引和元素。

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

然而,如果你在非razor环境中做一个单独的操作(例如,一个可以作为lambda提供的操作),这在非razor环境中不会是for/foreach语法的可靠替代。

这个答案是:游说c#语言团队获得直接的语言支持。

开头的答案是这样的:

显然,索引的概念与的概念是不同的 枚举,并且不能执行。

虽然目前的c#语言版本(2020年)确实如此,但这并不是CLR/ language的概念限制,它是可以做到的。

微软c#语言开发团队可以通过添加对新的Interface IIndexedEnumerable的支持来创建一个新的c#语言特性

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

如果使用foreach()并且存在var索引,则编译器期望项集合声明IIndexedEnumerable接口。如果没有接口,编译器可以用IndexedEnumerable对象对源代码进行polyfill包装,该对象将添加到代码中用于跟踪索引。

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

稍后,可以将CLR更新为具有内部索引跟踪,这仅在指定了with关键字且源代码没有直接实现IIndexedEnumerable时使用

Why:

Foreach看起来更好,在业务应用程序中,Foreach循环很少成为性能瓶颈 Foreach可以更有效地使用内存。拥有一个函数管道,而不是在每一步都转换为新的集合。当CPU缓存故障和垃圾收集更少时,谁会关心它是否使用了更多的CPU周期呢? 要求编码器添加索引跟踪代码,破坏了它的美感 它很容易实现(请微软),并且向后兼容

虽然这里的大多数人都不是微软的员工,但这是一个正确的答案,你可以游说微软增加这样一个功能。您已经可以使用扩展函数构建自己的迭代器并使用元组,但微软可以在语法上略加改进以避免使用扩展函数

foreach用于迭代实现IEnumerable的集合。它通过在集合上调用GetEnumerator来实现这一点,该集合将返回一个Enumerator。

这个枚举器有一个方法和一个属性:

MoveNext () 当前的

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

索引的概念与枚举的概念是不同的,不能这样做。

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

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

除非你的集合可以通过某种方法返回对象的索引,否则唯一的方法就是使用像你的例子中那样的计数器。

然而,当使用索引时,唯一合理的答案是使用for循环。其他任何事情都会带来代码的复杂性,更不用说时间和空间的复杂性了。

这是我刚想到的解决这个问题的办法

原始代码:

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

更新代码

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

扩展方法:

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