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

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

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

当前回答

我只是遇到了这个问题,但在我的情况下思考这个问题给出了最好的解决方案,与预期的解决方案无关。

It could be quite a common case, basically, I'm reading from one source list and creating objects based on them in a destination list, however, I have to check whether the source items are valid first and want to return the row of any error. At first-glance, I want to get the index into the enumerator of the object at the Current property, however, as I am copying these elements, I implicitly know the current index anyway from the current destination. Obviously it depends on your destination object, but for me it was a List, and most likely it will implement ICollection.

i.e.

var destinationList = new List<someObject>();
foreach (var item in itemList)
{
  var stringArray = item.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);

  if (stringArray.Length != 2)
  {
    //use the destinationList Count property to give us the index into the stringArray list
    throw new Exception("Item at row " + (destinationList.Count + 1) + " has a problem.");
  }
  else
  {
    destinationList.Add(new someObject() { Prop1 = stringArray[0], Prop2 = stringArray[1]});
  }
}

我认为,虽然不总是适用,但常常足以值得一提。

不管怎样,关键是有时在你的逻辑中已经有了一个不明显的解决方案……

其他回答

它只适用于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中所做的事情,这样更简单,适应性更强。

使用计数器变量并没有什么错。事实上,无论使用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更好,因为您仍然在运行单线程的更难看的语法。

像这样的怎么样?注意,如果myEnumerable为空,myDelimitedString可能为空。

IEnumerator enumerator = myEnumerable.GetEnumerator();
string myDelimitedString;
string current = null;

if( enumerator.MoveNext() )
    current = (string)enumerator.Current;

while( null != current)
{
    current = (string)enumerator.Current; }

    myDelimitedString += current;

    if( enumerator.MoveNext() )
        myDelimitedString += DELIMITER;
    else
        break;
}

我就是这么做的,这很简单,但如果你在循环body obj中做了很多。价值,它很快就会过时。

foreach(var obj in collection.Select((item, index) => new { Index = index, Value = item }) {
    string foo = string.Format("Something[{0}] = {1}", obj.Index, obj.Value);
    ...
}

这里是这个问题的另一个解决方案,重点是使语法尽可能接近标准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循环,但这不是这个用例中的主要问题。