如何获取foreach循环当前迭代的索引?
在C中是否有一些我没有遇到过的稀有语言构造(例如我最近学到的一些,有些是关于Stack Overflow的)得到了一个代表foreach循环当前迭代的值?
例如,根据情况,我目前正在执行以下操作:
int i=0; foreach (Object o in collection) { // ... i++; }
解决方案
回答
除非集合可以通过某种方法返回对象的索引,否则唯一的方法就是使用示例中的计数器。
但是,使用索引时,对该问题的唯一合理答案是使用for循环。其他任何事情都会导致代码复杂性,更不用说时间和空间的复杂性了。
回答
我不相信有一种方法可以获取foreach循环当前迭代的值。盘点自己,似乎是最好的方法。
请问,我们为什么想知道?
看来我们大多数会想做以下三件事之一:
1)从集合中获取对象,但是在这种情况下,我们已经拥有了它。
2)计算对象以便以后进行后期处理...这些集合具有Count属性,我们可以利用它。
3)根据对象在循环中的顺序为其设置属性...尽管在将对象添加到集合中时可以很容易地对其进行设置。
回答
" foreach"用于迭代实现" IEnumerable"的集合。它通过在集合上调用GetEnumerator
来实现,它将返回一个Enumerator
。
此枚举器具有一个方法和一个属性:
- MoveNext()
- 当前的
Current返回枚举器当前所在的对象,MoveNext将Current更新为下一个对象。
显然,索引的概念与枚举的概念是陌生的,无法做到。
因此,大多数集合都可以使用索引器和for循环构造遍历。
与使用局部变量跟踪索引相比,在这种情况下,我非常喜欢使用for循环。
回答
可以做这样的事情:
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)); } }
回答
它仅适用于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();
@乔纳森我没有说这是一个很好的答案,我只是说这只是表明有可能做到他所要求的:)
@Graphain我不希望它很快,但我不确定它是如何工作的,它每次可以在整个列表中重复以找到匹配的对象,这将是一个比较对象。
也就是说,List可以保留每个对象的索引以及计数。
乔纳森(Jonathan)似乎有更好的主意,如果他能详细说明?
最好只统计一下我们要达到的目标,而且更简单,适应性更强。
回答
字面答案-警告,性能可能不如仅使用int
来跟踪索引那样好。至少它比使用IndexOf
更好。
我们只需要使用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); }
回答
我不同意" for"循环在大多数情况下是更好的选择的意见。
" foreach"是一个有用的构造,在任何情况下都不能被" for"循环替代。
例如,如果我们有一个DataReader并使用" foreach"循环遍历所有记录,它将自动调用Dispose方法并关闭阅读器(然后可以自动关闭连接)。因此,这样做更安全,因为即使我们忘记关闭阅读器,它也可以防止连接泄漏。
(确保总是关闭读者是一个好习惯,但是如果我们不这样做,编译器将不会捕获它,我们不能保证已经关闭了所有读者,但是可以通过进入来使它更有可能不会泄漏连接使用foreach的习惯。)
可能还有其他一些例子,其中隐式调用Dispose方法很有用。
回答
这是我刚想出的解决方案
原始代码:
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; }
回答
int index; foreach (Object o in collection) { index = collection.indexOf(o); }
这将适用于支持IList
的集合。
回答
我们可以将原始枚举数与另一个包含索引信息的枚举数包装在一起。
foreach (var item in ForEachHelper.WithIndex(collection)) { Console.Write("Index=" + item.Index); Console.Write(";Value= " + item.Value); Console.Write(";IsLast=" + item.IsLast); Console.WriteLine(); }
这是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; } } }
回答
最好像这样使用关键字continue
安全构造
int i=-1; foreach (Object o in collection) { ++i; //... continue; //<--- safe to call, index will be increased //... }
回答
这就是我的操作方式,这很简单/简洁,但是如果我们在循环体obj.Value中做很多事情,它会很快变旧。
foreach(var obj in collection.Select((item, index) => new { Index = index, Value = item }) { string foo = string.Format("Something[{0}] = {1}", obj.Index, obj.Value); ... }
回答
我对这个问题的解决方案是扩展方法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));
回答
这样的事情怎么样?请注意,如果myEnumerable为空,则myDelimitedString可以为null。
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; }