我经常在编程过程中遇到这种情况,我想在foreach中有一个循环计数索引,并且必须创建一个整数,使用它,递增等。如果引入一个关键字是foreach内部的循环计数?它也可以在其他循环中使用。
这是否与关键字的使用背道而驰,因为编译器将不允许该关键字在循环结构中的任何地方使用?
for ()
循环。
我经常在编程过程中遇到这种情况,我想在foreach中有一个循环计数索引,并且必须创建一个整数,使用它,递增等。如果引入一个关键字是foreach内部的循环计数?它也可以在其他循环中使用。
这是否与关键字的使用背道而驰,因为编译器将不允许该关键字在循环结构中的任何地方使用?
for ()
循环。
Answers:
如果需要在循环中进行循环计数,foreach
为什么不使用常规for
循环。该foreach
环路的目的是使特定用途for
循环简单。听起来您似乎遇到了这样的情况,即简单性foreach
不再有益。
IEnumerable.Count
可能会很昂贵(或者是不可能的,如果是一次枚举)。您需要先知道基础源是什么,然后才能安全地进行操作。
for
循环给出了更好的可读性代码(理论上,编译器优化通常是过早的优化)。
您可以使用像这样的匿名类型
foreach (var item in items.Select((v, i) => new { Value = v, Index = i }))
{
Console.WriteLine("Item value is {0} and index is {1}.", item.Value, item.Index);
}
这类似于Python的enumerate
功能
for i, v in enumerate(items):
print v, i
我只是按要求在此处添加Dan Finch的答案。
不要给我积分,给丹·芬奇积分:-)
解决方案是编写以下通用扩展方法:
public static void Each<T>( this IEnumerable<T> ie, Action<T, int> action )
{
var i = 0;
foreach ( var e in ie ) action( e, i++ );
}
用法如下:
var strings = new List<string>();
strings.Each( ( str, n ) =>
{
// hooray
} );
我对此可能是错的,但是我一直认为foreach循环的重点是简化C ++的STL时代的迭代,即
for(std::stltype<typename>::iterator iter = stl_type_instance.begin(); iter != stl_type_instance.end(); iter++)
{
//dereference iter to use each object.
}
将此与.NET在foreach中对IEnumerable的使用进行比较
foreach(TypeName instance in DescendentOfIEnumerable)
{
//use instance to use the object.
}
后者要简单得多,并且避免了许多旧的陷阱。此外,他们似乎遵循几乎相同的规则。例如,您不能在循环内更改IEnumerable的内容(如果您以STL的方式考虑它,则更有意义)。但是,for循环的逻辑目的通常不同于迭代。
for(IEnumerator e=collection.GetEnumerator;e.MoveNext();) { ... }
如果相关集合是IList<T>
,则可以简单地for
与索引一起使用:
for (int i = 0; i < collection.Count; i++)
{
var item = collection[i];
// …
}
如果不是,则可以使用Select()
,它具有一个重载,可以为您提供索引。
如果这也不适合,我认为手动维护该索引就足够简单了,它只是两行非常短的代码。为此专门创建关键字将是一个过大的选择。
int i = 0;
foreach (var item in collection)
{
// …
i++;
}
foo(++i)
或foo(i++)
根据需要的索引进行操作。
如果您只知道该集合是一个IEnumerable,但需要跟踪到目前为止已处理了多少个元素(完成后便是总数),则可以在基本的for循环中添加几行:
var coll = GetMyCollectionAsAnIEnumerable();
var idx = 0;
for(var e = coll.GetEnumerator(); e.MoveNext(); idx++)
{
var elem = e.Current;
//use elem and idx as you please
}
您还可以将增量索引变量添加到foreach中:
var i=0;
foreach(var elem in coll)
{
//do your thing, then...
i++;
}
如果您想让它看起来更优雅,可以定义一个或两个扩展方法来“隐藏”这些细节:
public static void ForEach<T>(this IEnumerable<T> input, Action<T> action)
{
foreach(T elem in input)
action(elem);
}
public static void ForEach<T>(this IEnumerable<T> input, Action<T, int> action)
{
var idx = 0;
foreach(T elem in input)
action(elem, idx++); //post-increment happens after parameter-passing
}
//usage of the index-supporting method
coll.ForEach((e, i)=>Console.WriteLine("Element " + (i+1) + ": " + e.ToString()));