在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++;
}
当前回答
我不同意在大多数情况下使用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;
}
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.
}
foreach用于迭代实现IEnumerable的集合。它通过在集合上调用GetEnumerator来实现这一点,该集合将返回一个Enumerator。
这个枚举器有一个方法和一个属性:
MoveNext () 当前的
Current返回Enumerator当前所在的对象,MoveNext将Current更新为下一个对象。
索引的概念与枚举的概念是不同的,不能这样做。
因此,大多数集合都可以使用索引器和for循环构造来遍历。
在这种情况下,与使用局部变量跟踪索引相比,我更喜欢使用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)
最好像这样使用关键字继续安全构造
int i=-1;
foreach (Object o in collection)
{
++i;
//...
continue; //<--- safe to call, index will be increased
//...
}