在c#中是否有一些我没有遇到过的罕见的语言构造(比如我最近学过的一些,一些在Stack Overflow上)来获得表示foreach循环的当前迭代的值?
例如,我目前根据具体情况做这样的事情:
int i = 0;
foreach (Object o in collection)
{
// ...
i++;
}
在c#中是否有一些我没有遇到过的罕见的语言构造(比如我最近学过的一些,一些在Stack Overflow上)来获得表示foreach循环的当前迭代的值?
例如,我目前根据具体情况做这样的事情:
int i = 0;
foreach (Object o in collection)
{
// ...
i++;
}
当前回答
出于兴趣,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语法的可靠替代。
其他回答
我对这个问题的解决方案是一个扩展方法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));
它只适用于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中所做的事情,这样更简单,适应性更强。
字面答案——警告,性能可能不如仅使用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循环。其他任何事情都会带来代码的复杂性,更不用说时间和空间的复杂性了。
为什么?!
如果你使用List,最简单的方法是使用for而不是foreach:
for (int i = 0 ; i < myList.Count ; i++)
{
// Do something...
}
或者如果你想使用foreach:
foreach (string m in myList)
{
// Do something...
}
你可以用它来知道每个循环的索引:
myList.indexOf(m)