在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++;
}
当前回答
如果集合是列表,则可以使用list。IndexOf,如:
foreach (Object o in collection)
{
// ...
@collection.IndexOf(o)
}
其他回答
可以这样做:
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));
}
}
我不相信有一种方法可以获得foreach循环当前迭代的值。数数自己,似乎是最好的办法。
我能问一下,你为什么想知道?
看起来你最有可能做以下三件事中的一件:
1)从集合中获取对象,但在这种情况下,您已经拥有它。
2)为后期处理计算对象…集合有一个可以使用的Count属性。
3)根据对象在循环中的顺序设置它的属性…尽管您可以在将对象添加到集合时轻松地进行设置。
这个答案是:游说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周期呢? 要求编码器添加索引跟踪代码,破坏了它的美感 它很容易实现(请微软),并且向后兼容
虽然这里的大多数人都不是微软的员工,但这是一个正确的答案,你可以游说微软增加这样一个功能。您已经可以使用扩展函数构建自己的迭代器并使用元组,但微软可以在语法上略加改进以避免使用扩展函数
这里是这个问题的另一个解决方案,重点是使语法尽可能接近标准foreach。
如果你想让你的视图在MVC中看起来漂亮干净,这种构造是有用的。例如,不要用通常的方式来写(这很难格式化):
<%int i=0;
foreach (var review in Model.ReviewsList) { %>
<div id="review_<%=i%>">
<h3><%:review.Title%></h3>
</div>
<%i++;
} %>
你可以这样写:
<%foreach (var review in Model.ReviewsList.WithIndex()) { %>
<div id="review_<%=LoopHelper.Index()%>">
<h3><%:review.Title%></h3>
</div>
<%} %>
我写了一些辅助方法来实现这个功能:
public static class LoopHelper {
public static int Index() {
return (int)HttpContext.Current.Items["LoopHelper_Index"];
}
}
public static class LoopHelperExtensions {
public static IEnumerable<T> WithIndex<T>(this IEnumerable<T> that) {
return new EnumerableWithIndex<T>(that);
}
public class EnumerableWithIndex<T> : IEnumerable<T> {
public IEnumerable<T> Enumerable;
public EnumerableWithIndex(IEnumerable<T> enumerable) {
Enumerable = enumerable;
}
public IEnumerator<T> GetEnumerator() {
for (int i = 0; i < Enumerable.Count(); i++) {
HttpContext.Current.Items["LoopHelper_Index"] = i;
yield return Enumerable.ElementAt(i);
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
在非web环境中,你可以使用static来代替HttpContext.Current.Items。
这本质上是一个全局变量,因此不能嵌套多个WithIndex循环,但这不是这个用例中的主要问题。
// using foreach loop how to get index number:
foreach (var result in results.Select((value, index) => new { index, value }))
{
// do something
}