是否有一些罕见的语言构造我没有遇到过(比如我最近学到的一些,有些是关于Stack Overflow)在C#中得到一个表示foreach循环当前迭代的值?
例如,我目前根据具体情况做这样的事情:
int i = 0;
foreach (Object o in collection)
{
// ...
i++;
}
Run Code Online (Sandbox Code Playgroud)
小智 602
Ian Mercer在Phil Haack的博客上发布了类似的解决方案:
foreach (var item in Model.Select((value, i) => new { i, value }))
{
var value = item.value;
var index = item.i;
}
Run Code Online (Sandbox Code Playgroud)
这将通过使用Linq的重载来获取item(item.value)及其index(item.i):Select
函数[select Select]的第二个参数表示源元素的索引.
该new { i, value }是创建一个新的匿名对象.
ValueTuple如果您使用的是C#7.0或更高版本,则可以避免使用堆分配:
foreach (var item in Model.Select((value, i) => ( value, i )))
{
var value = item.value;
var index = item.i;
}
Run Code Online (Sandbox Code Playgroud)
您还可以item.通过使用自动解构来消除:
<ol>
foreach ((MyType value, Int32 i) in Model.Select((value, i) => ( value, i )))
{
<li id="item_@i">@value</li>
}
</ol>
Run Code Online (Sandbox Code Playgroud)
Fly*_*wat 515
该foreach是遍历实现集合IEnumerable.它通过调用GetEnumerator集合来执行此操作,该集合将返回一个Enumerator.
此枚举器有一个方法和属性:
Current返回Enumerator当前所在的对象,MoveNext更新Current下一个对象.
索引的概念对于枚举的概念是陌生的,并且不能完成.
因此,大多数集合都可以使用索引器和for循环结构遍历.
与使用局部变量跟踪索引相比,我更倾向于在这种情况下使用for循环.
use*_*562 121
最后,C#7有一个很好的语法来获取foreach循环内的索引(即元组):
foreach (var (item, index) in collection.WithIndex())
{
Debug.WriteLine($"{index}: {item}");
}
Run Code Online (Sandbox Code Playgroud)
需要一点扩展方法:
public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)
=> self.Select((item, index) => (item, index));
Run Code Online (Sandbox Code Playgroud)
Bra*_*son 111
可以做这样的事情:
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));
}
}
Run Code Online (Sandbox Code Playgroud)
mik*_*son 91
我不同意评论说for在大多数情况下循环是更好的选择.
foreach是一个有用的构造,并不是for在所有情况下都可以通过循环替换.
例如,如果您有一个DataReader并使用foreach它循环遍历所有记录,它会自动调用Dispose方法并关闭阅读器(然后可以自动关闭连接).因此,即使您忘记关闭阅读器,也可以更安全,因为它可以防止连接泄漏.
(当然,总是关闭读者是好的做法,但是如果你不这样做,编译器就不会抓住它 - 你不能保证你已经关闭了所有的读者,但是你可以更有可能通过获取而不会泄漏连接习惯使用foreach.)
可能存在该Dispose方法的隐式调用有用的其他示例.
Amy*_*y B 59
字面答案 - 警告,性能可能不如仅使用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);
}
Run Code Online (Sandbox Code Playgroud)
Gez*_*zim 32
使用@FlySwat的答案,我提出了这个解决方案:
//var list = new List<int> { 1, 2, 3, 4, 5, 6 }; // Your sample collection
var listEnumerator = list.GetEnumerator(); // Get enumerator
for (var i = 0; listEnumerator.MoveNext() == true; i++)
{
int currentItem = listEnumerator.Current; // Get current item.
//Console.WriteLine("At index {0}, item is {1}", i, currentItem); // Do as you wish with i and currentItem
}
Run Code Online (Sandbox Code Playgroud)
您使用枚举器GetEnumerator,然后使用for循环循环.然而,诀窍是使循环的条件listEnumerator.MoveNext() == true.
由于MoveNext枚举器的方法在有下一个元素时返回true并且可以访问它,因此当我们用尽迭代的元素时,使循环条件使循环停止.
Pav*_*vel 30
使用LINQ,C#7和System.ValueTupleNuGet包,您可以这样做:
foreach (var (value, index) in collection.Select((v, i)=>(v, i))) {
Console.WriteLine(value + " is at index " + index);
}
Run Code Online (Sandbox Code Playgroud)
您可以使用常规foreach构造,并且能够直接访问值和索引,而不是作为对象的成员,并且仅将两个字段保留在循环的范围内.出于这些原因,我相信如果您能够使用C#7和C#7,这是最好的解决方案System.ValueTuple.
Bri*_*eon 25
您可以将原始枚举器包含在包含索引信息的另一个枚举器中.
foreach (var item in ForEachHelper.WithIndex(collection))
{
Console.Write("Index=" + item.Index);
Console.Write(";Value= " + item.Value);
Console.Write(";IsLast=" + item.IsLast);
Console.WriteLine();
}
Run Code Online (Sandbox Code Playgroud)
这是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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
Dav*_*ock 25
使用计数器变量没有任何问题.实际上,无论你使用for,foreach while还是do一个计数器变量都必须在某处声明并递增.
如果你不确定你是否有一个适当索引的集合,请使用这个习惯用法:
var i = 0;
foreach (var e in collection) {
// Do stuff with 'e' and 'i'
i++;
}
Run Code Online (Sandbox Code Playgroud)
如果你知道你的可索引集合是O(1)用于索引访问(它Array可能用于List<T>(可能是文档没有说),但是不一定用于其他类型(例如LinkedList)),否则使用这个:
// Hope the JIT compiler optimises read of the 'Count' property!
for (var i = 0; i < collection.Count; i++) {
var e = collection[i];
// Do stuff with 'e' and 'i'
}
Run Code Online (Sandbox Code Playgroud)
永远不需要IEnumerator通过调用MoveNext()和询问来"手动"操作Current- foreach为您节省特别麻烦......如果您需要跳过项目,只需continue在循环体中使用a .
而就的完整性,这取决于你是什么做用食指(以上结构提供了极大的灵活性),您可以使用并行LINQ:
// First, filter 'e' based on 'i',
// then apply an action to remaining 'e'
collection
.AsParallel()
.Where((e,i) => /* filter with e,i */)
.ForAll(e => { /* use e, but don't modify it */ });
// Using 'e' and 'i', produce a new collection,
// where each element incorporates 'i'
collection
.AsParallel()
.Select((e, i) => new MyWrapper(e, i));
Run Code Online (Sandbox Code Playgroud)
我们AsParallel()在上面使用,因为它已经是2014年了,我们希望充分利用这些多核来加快速度.此外,对于"顺序"LINQ,您只能获得一个ForEach()扩展方法List<T>和Array ...并且不清楚使用它是否比执行简单更好foreach,因为您仍然在运行单线程以获得更粗糙的语法.
mat*_*at3 18
这是我刚刚提出的解决这个问题的解决方案
原始代码:
int index=0;
foreach (var item in enumerable)
{
blah(item, index); // some code that depends on the index
index++;
}
Run Code Online (Sandbox Code Playgroud)
更新的代码
enumerable.ForEach((item, index) => blah(item, index));
Run Code Online (Sandbox Code Playgroud)
扩展方法:
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;
}
Run Code Online (Sandbox Code Playgroud)
con*_*rio 13
只需添加自己的索引.把事情简单化.
int i = 0;
foreach (var item in Collection)
{
item.index = i;
++i;
}
Run Code Online (Sandbox Code Playgroud)
Pau*_*ell 12
C#7最终为我们提供了一种优雅的方法:
static class Extensions
{
public static IEnumerable<(int, T)> Enumerate<T>(
this IEnumerable<T> input,
int start = 0
)
{
int i = start;
foreach (var t in input)
{
yield return (i++, t);
}
}
}
class Program
{
static void Main(string[] args)
{
var s = new string[]
{
"Alpha",
"Bravo",
"Charlie",
"Delta"
};
foreach (var (i, t) in s.Enumerate())
{
Console.WriteLine($"{i}: {t}");
}
}
}
Run Code Online (Sandbox Code Playgroud)
Par*_*rsa 10
为什么要foreach?!
如果您使用List,最简单的方法是使用for而不是foreach .
for (int i = 0 ; i < myList.Count ; i++)
{
// Do something...
}
Run Code Online (Sandbox Code Playgroud)
或者如果你想使用foreach:
foreach (string m in myList)
{
// Do something...
}
Run Code Online (Sandbox Code Playgroud)
你可以用它来表示每个循环的khow索引:
myList.indexOf(m)
Run Code Online (Sandbox Code Playgroud)
它只适用于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();
Run Code Online (Sandbox Code Playgroud)
@Jonathan我没有说这是一个很好的答案,我只是说它只是表明它可以做他所要求的:)
@Graphain我不希望它快速 - 我不完全确定它是如何工作的,它可以在每次重复整个列表中找到一个匹配的对象,这将是比较的确认.
也就是说,List可能会保留每个对象的索引以及计数.
乔纳森似乎有更好的主意,如果他会详细说明的话?
尽管如此,更简单,更具适应性,最好只计算你在foreach中所处的位置.
小智 9
// using foreach loop how to get index number:
foreach (var result in results.Select((value, index) => new { index, value }))
{
// do something
}
Run Code Online (Sandbox Code Playgroud)
小智 8
int index;
foreach (Object o in collection)
{
index = collection.indexOf(o);
}
Run Code Online (Sandbox Code Playgroud)
这适用于集合支持IList.
我就是这样做的,它的简洁/简洁很好,但是如果你在循环体中做了很多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);
...
}
Run Code Online (Sandbox Code Playgroud)
主要答案指出:
“显然,索引的概念与枚举的概念是陌生的,无法做到。”
尽管当前的C#版本是这样,但这不是概念上的限制。
MS创建新的C#语言功能可以解决此问题,并支持新的Interface IIndexedEnumerable
foreach (var item in collection with var index)
{
Console.WriteLine("Iteration {0} has value {1}", index, item);
}
//or, building on @user1414213562's answer
foreach (var (item, index) in collection)
{
Console.WriteLine("Iteration {0} has value {1}", index, item);
}
Run Code Online (Sandbox Code Playgroud)
如果foreach传递了IEnumerable且无法解析IIndexedEnumerable,但是使用var index进行询问,则C#编译器可以使用IndexedEnumerable对象包装源,该对象添加了用于跟踪索引的代码。
interface IIndexedEnumerable<T> : IEnumerable<T>
{
//Not index, because sometimes source IEnumerables are transient
public long IterationNumber { get; }
}
Run Code Online (Sandbox Code Playgroud)
为什么:
尽管此处的大多数人不是MS,但这是正确的答案,您可以游说MS来添加此类功能。您已经可以使用扩展功能构建自己的迭代器并使用元组,但是MS可以撒上语法糖以避免扩展功能
小智 6
最好使用continue像这样的关键字安全构造
int i=-1;
foreach (Object o in collection)
{
++i;
//...
continue; //<--- safe to call, index will be increased
//...
}
Run Code Online (Sandbox Code Playgroud)
如果集合是列表,则可以使用List.IndexOf,如下所示:
foreach (Object o in collection)
{
// ...
@collection.IndexOf(o)
}
Run Code Online (Sandbox Code Playgroud)
小智 5
您可以像这样编写循环:
var s = "ABCDEFG";
foreach (var item in s.GetEnumeratorWithIndex())
{
System.Console.WriteLine("Character: {0}, Position: {1}", item.Value, item.Index);
}
Run Code Online (Sandbox Code Playgroud)
添加以下结构和扩展方法后。
结构体和扩展方法封装了 Enumerable.Select 功能。
public struct ValueWithIndex<T>
{
public readonly T Value;
public readonly int Index;
public ValueWithIndex(T value, int index)
{
this.Value = value;
this.Index = index;
}
public static ValueWithIndex<T> Create(T value, int index)
{
return new ValueWithIndex<T>(value, index);
}
}
public static class ExtensionMethods
{
public static IEnumerable<ValueWithIndex<T>> GetEnumeratorWithIndex<T>(this IEnumerable<T> enumerable)
{
return enumerable.Select(ValueWithIndex<T>.Create);
}
}
Run Code Online (Sandbox Code Playgroud)