带索引的 foreach

C # 是否与 Python 的 enumerate()和 Ruby 的 each_with_index等价?

249129 次浏览

C # foreach 没有内置的索引。您需要在 foreach 循环之外添加一个整数,并且每次递增它。

int i = -1;
foreach (Widget w in widgets)
{
i++;
// do something
}

或者,您可以使用标准的 for 循环,如下所示:

for (int i = 0; i < widgets.Length; i++)
{
w = widgets[i];
// do something
}

这取决于您使用的类。

例如,Dictionary < (Of < (TKey,TValue >) >) Class 支持此

Dictionary < (Of < (TKey,TValue >) >)泛型类提供了从一组键到一组值的映射。

为了进行枚举,字典中的每个项都被视为表示值及其键的 KeyValuePair < (Of < (TKey,TValue >) >)结构。返回项的顺序未定义。

Foreach (myDictionary 中的 KeyValuePair kvp){ ... }

您可以执行以下操作

foreach (var it in someCollection.Select((x, i) => new { Value = x, Index = i }) )
{
if (it.Index > SomeNumber) //
}

这将为集合中的每个条目创建一个匿名类型值

  • Value: 集合中的原始值
  • Index: 集合中的索引

除了已经给出的 LINQ 答案,我还有一个 “智能枚举”类,它允许您获得索引和“ first/last”-ness。它在语法方面有点难看,但是您可能会发现它很有用。

我们可以使用非泛型类型中的静态方法来改进类型推断,隐式类型也会有所帮助。

我的解决方案涉及我为通用实用程序创建的一个简单的 Pair 类,它在操作上与框架类 KeyValuePair 基本相同。然后,我为 IEnumable 创建了两个扩展函数,称为 Ordinate (来自集合论术语“ 顺序”)。

这些函数将为每个项目返回一个包含索引和项目本身的 Pair 对象。

public static IEnumerable<Pair<Int32, X>> Ordinate<X>(this IEnumerable<X> lhs)
{
return lhs.Ordinate(0);
}


public static IEnumerable<Pair<Int32, X>> Ordinate<X>(this IEnumerable<X> lhs, Int32 initial)
{
Int32 index = initial - 1;


return lhs.Select(x => new Pair<Int32, X>(++index, x));
}

不,没有。

正如其他人所展示的,有很多方法可以模拟 Ruby 的行为。但是,有可能有一个实现 数不胜数的类型不公开索引。

我为此保留了这个扩展方法:

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
});

或者考虑到类似 break的行为:

public static bool Each<T>(this IEnumerable<T> ie, Func<T, int, bool> action)
{
int i = 0;
foreach (T e in ie) if (!action(e, i++)) return false;
return true;
}


var strings = new List<string>() { "a", "b", "c" };


bool iteratedAll = strings.Each ((str, n)) =>
{
if (str == "b") return false;
return true;
});

我刚想出了一个有趣的解决办法:

public class DepthAware<T> : IEnumerable<T>
{
private readonly IEnumerable<T> source;


public DepthAware(IEnumerable<T> source)
{
this.source = source;
this.Depth = 0;
}


public int Depth { get; private set; }


private IEnumerable<T> GetItems()
{
foreach (var item in source)
{
yield return item;
++this.Depth;
}
}


public IEnumerator<T> GetEnumerator()
{
return GetItems().GetEnumerator();
}


IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}


// Generic type leverage and extension invoking
public static class DepthAware
{
public static DepthAware<T> AsDepthAware<T>(this IEnumerable<T> source)
{
return new DepthAware<T>(source);
}


public static DepthAware<T> New<T>(IEnumerable<T> source)
{
return new DepthAware<T>(source);
}
}

用法:

var chars = new[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'}.AsDepthAware();


foreach (var item in chars)
{
Console.WriteLine("Char: {0}, depth: {1}", item, chars.Depth);
}

这是你的收藏

var values = new[] {6, 2, 8, 45, 9, 3, 0};

为此集合创建一系列索引

var indexes = Enumerable.Range(0, values.Length).ToList();

使用该范围对索引进行迭代

indexes.ForEach(i => values[i] += i);
indexes.ForEach(i => Console.Write("[{0}] = {1}", i, values[i]));

我喜欢使用 foreach,所以我创建了一个扩展方法和结构:

public struct EnumeratedInstance<T>
{
public long cnt;
public T item;
}


public static IEnumerable<EnumeratedInstance<T>> Enumerate<T>(this IEnumerable<T> collection)
{
long counter = 0;
foreach (var item in collection)
{
yield return new EnumeratedInstance<T>
{
cnt = counter,
item = item
};
counter++;
}
}

并举例说明:

foreach (var ii in new string[] { "a", "b", "c" }.Enumerate())
{
Console.WriteLine(ii.item + ii.cnt);
}

一个好处是,如果您习惯了 Python 语法,您仍然可以使用它:

foreach (var ii in Enumerate(new string[] { "a", "b", "c" }))