在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语法的可靠替代。
其他回答
Ian Mercer在Phil Haack的博客上发表了一个类似的解决方案:
foreach (var item in Model.Select((value, i) => new { i, value }))
{
var value = item.value;
var index = item.i;
}
通过重载LINQ的Select函数,你可以得到item (item.value)和它的索引(item.i):
函数[在Select内部]的第二个参数表示源元素的索引。
新的{i, value}正在创建一个新的匿名对象。
如果你使用c# 7.0或更高版本,可以使用ValueTuple来避免堆分配:
foreach (var item in Model.Select((value, i) => ( value, i )))
{
var value = item.value;
var index = item.i;
}
你也可以删除这个项目。通过使用自动解构:
foreach (var (value, i) in Model.Select((value, i) => ( value, i )))
{
// Access `value` and `i` directly here.
}
最后,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而不是任何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中所做的事情,这样更简单,适应性更强。
我不确定你试图用基于问题的索引信息做什么。然而,在c#中,你通常可以使用IEnumerable。Select方法从您想要的任何内容中获取索引。例如,我可以使用类似这样的方法来判断一个值是奇数还是偶数。
string[] names = { "one", "two", "three" };
var oddOrEvenByName = names
.Select((name, index) => new KeyValuePair<string, int>(name, index % 2))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
这将根据列表中的项是奇数(1)还是偶数(0)的名称为您提供一个字典。
我不相信有一种方法可以获得foreach循环当前迭代的值。数数自己,似乎是最好的办法。
我能问一下,你为什么想知道?
看起来你最有可能做以下三件事中的一件:
1)从集合中获取对象,但在这种情况下,您已经拥有它。
2)为后期处理计算对象…集合有一个可以使用的Count属性。
3)根据对象在循环中的顺序设置它的属性…尽管您可以在将对象添加到集合时轻松地进行设置。