在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++;
}
当前回答
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}");
}
}
}
其他回答
为什么?!
如果你使用List,最简单的方法是使用for而不是foreach:
for (int i = 0 ; i < myList.Count ; i++)
{
// Do something...
}
或者如果你想使用foreach:
foreach (string m in myList)
{
// Do something...
}
你可以用它来知道每个循环的索引:
myList.indexOf(m)
你可以这样写你的循环:
var s = "ABCDEFG";
foreach (var item in s.GetEnumeratorWithIndex())
{
System.Console.WriteLine("Character: {0}, Position: {1}", item.Value, item.Index);
}
之后添加如下结构和扩展方法。
结构和扩展方法封装了Enumerable。选择功能。
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);
}
}
这个答案是:游说c#语言团队获得直接的语言支持。
开头的答案是这样的:
显然,索引的概念与的概念是不同的 枚举,并且不能执行。
虽然目前的c#语言版本(2020年)确实如此,但这并不是CLR/ language的概念限制,它是可以做到的。
微软c#语言开发团队可以通过添加对新的Interface IIndexedEnumerable的支持来创建一个新的c#语言特性
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);
}
如果使用foreach()并且存在var索引,则编译器期望项集合声明IIndexedEnumerable接口。如果没有接口,编译器可以用IndexedEnumerable对象对源代码进行polyfill包装,该对象将添加到代码中用于跟踪索引。
interface IIndexedEnumerable<T> : IEnumerable<T>
{
//Not index, because sometimes source IEnumerables are transient
public long IterationNumber { get; }
}
稍后,可以将CLR更新为具有内部索引跟踪,这仅在指定了with关键字且源代码没有直接实现IIndexedEnumerable时使用
Why:
Foreach看起来更好,在业务应用程序中,Foreach循环很少成为性能瓶颈 Foreach可以更有效地使用内存。拥有一个函数管道,而不是在每一步都转换为新的集合。当CPU缓存故障和垃圾收集更少时,谁会关心它是否使用了更多的CPU周期呢? 要求编码器添加索引跟踪代码,破坏了它的美感 它很容易实现(请微软),并且向后兼容
虽然这里的大多数人都不是微软的员工,但这是一个正确的答案,你可以游说微软增加这样一个功能。您已经可以使用扩展函数构建自己的迭代器并使用元组,但微软可以在语法上略加改进以避免使用扩展函数
// using foreach loop how to get index number:
foreach (var result in results.Select((value, index) => new { index, value }))
{
// do something
}
最后,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));
}