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

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

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

当前回答

它只适用于List而不是任何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我没有说这是一个很好的答案,我只是说这只是表明,他要求的事情是可以做到的:)

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

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

乔纳森似乎有更好的主意,能详细说说吗?

不过,最好只是记录一下你在foreach中所做的事情,这样更简单,适应性更强。

其他回答

可以这样做:

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

这里是这个问题的另一个解决方案,重点是使语法尽可能接近标准foreach。

如果你想让你的视图在MVC中看起来漂亮干净,这种构造是有用的。例如,不要用通常的方式来写(这很难格式化):

 <%int i=0;
 foreach (var review in Model.ReviewsList) { %>
    <div id="review_<%=i%>">
        <h3><%:review.Title%></h3>                      
    </div>
    <%i++;
 } %>

你可以这样写:

 <%foreach (var review in Model.ReviewsList.WithIndex()) { %>
    <div id="review_<%=LoopHelper.Index()%>">
        <h3><%:review.Title%></h3>                      
    </div>
 <%} %>

我写了一些辅助方法来实现这个功能:

public static class LoopHelper {
    public static int Index() {
        return (int)HttpContext.Current.Items["LoopHelper_Index"];
    }       
}

public static class LoopHelperExtensions {
    public static IEnumerable<T> WithIndex<T>(this IEnumerable<T> that) {
        return new EnumerableWithIndex<T>(that);
    }

    public class EnumerableWithIndex<T> : IEnumerable<T> {
        public IEnumerable<T> Enumerable;

        public EnumerableWithIndex(IEnumerable<T> enumerable) {
            Enumerable = enumerable;
        }

        public IEnumerator<T> GetEnumerator() {
            for (int i = 0; i < Enumerable.Count(); i++) {
                HttpContext.Current.Items["LoopHelper_Index"] = i;
                yield return Enumerable.ElementAt(i);
            }
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
    }

在非web环境中,你可以使用static来代替HttpContext.Current.Items。

这本质上是一个全局变量,因此不能嵌套多个WithIndex循环,但这不是这个用例中的主要问题。

最后,c# 7有一个不错的语法,用于在foreach循环(即元组)中获取索引:

foreach (var (item, index) in collection.WithIndex())
{
    Debug.WriteLine($"{index}: {item}");
}

需要一个小扩展方法:

using System.Collections.Generic;

public static class EnumExtension {
    public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)       
       => self.Select((item, index) => (item, index));
}

为什么?!

如果你使用List,最简单的方法是使用for而不是foreach:

for (int i = 0 ; i < myList.Count ; i++)
{
    // Do something...
}

或者如果你想使用foreach:

foreach (string m in myList)
{
     // Do something...
}

你可以用它来知道每个循环的索引:

myList.indexOf(m)

这并没有回答您的具体问题,但它确实为您的问题提供了一个解决方案:使用for循环来遍历对象集合。然后您将得到您正在处理的当前索引。

// Untested
for (int i = 0; i < collection.Count; i++)
{
    Console.WriteLine("My index is " + i);
}