在c#中是否有一些我没有遇到过的罕见的语言构造(比如我最近学过的一些,一些在Stack Overflow上)来获得表示foreach循环的当前迭代的值?

例如,我目前根据具体情况做这样的事情:

int i = 0;
foreach (Object o in collection)
{
    // ...
    i++;
}

当前回答

Ian Mercer在Phil Haack的博客上发表了一个类似的解决方案:

foreach (var item in Model.Select((value, i) => new { i, value }))
{
    var value = item.value;
    var index = item.i;
}

通过重载LINQ的Select函数,你可以得到item (item.value)和它的索引(item.i):

函数[在Select内部]的第二个参数表示源元素的索引。

新的{i, value}正在创建一个新的匿名对象。

如果你使用c# 7.0或更高版本,可以使用ValueTuple来避免堆分配:

foreach (var item in Model.Select((value, i) => ( value, i )))
{
    var value = item.value;
    var index = item.i;
}

你也可以删除这个项目。通过使用自动解构:

foreach (var (value, i) in Model.Select((value, i) => ( value, i )))
{
    // Access `value` and `i` directly here.
}

其他回答

我不相信有一种方法可以获得foreach循环当前迭代的值。数数自己,似乎是最好的办法。

我能问一下,你为什么想知道?

看起来你最有可能做以下三件事中的一件:

1)从集合中获取对象,但在这种情况下,您已经拥有它。

2)为后期处理计算对象…集合有一个可以使用的Count属性。

3)根据对象在循环中的顺序设置它的属性…尽管您可以在将对象添加到集合时轻松地进行设置。

可以这样做:

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

我不确定你试图用基于问题的索引信息做什么。然而,在c#中,你通常可以使用IEnumerable。Select方法从您想要的任何内容中获取索引。例如,我可以使用类似这样的方法来判断一个值是奇数还是偶数。

string[] names = { "one", "two", "three" };
var oddOrEvenByName = names
    .Select((name, index) => new KeyValuePair<string, int>(name, index % 2))
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

这将根据列表中的项是奇数(1)还是偶数(0)的名称为您提供一个字典。

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

这是我刚想到的解决这个问题的办法

原始代码:

int index=0;
foreach (var item in enumerable)
{
    blah(item, index); // some code that depends on the index
    index++;
}

更新代码

enumerable.ForEach((item, index) => blah(item, index));

扩展方法:

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