如何获取foreach循环当前迭代的索引?

时间:2020-03-05 18:47:25  来源:igfitidea点击:

在C中是否有一些我没有遇到过的稀有语言构造(例如我最近学到的一些,有些是关于Stack Overflow的)得到了一个代表foreach循环当前迭代的值?

例如,根据情况,我目前正在执行以下操作:

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

解决方案

回答

除非集合可以通过某种方法返回对象的索引,否则唯一的方法就是使用示例中的计数器。

但是,使用索引时,对该问题的唯一合理答案是使用for循环。其他任何事情都会导致代码复杂性,更不用说时间和空间的复杂性了。

回答

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

请问,我们为什么想知道?

看来我们大多数会想做以下三件事之一:

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

2)计算对象以便以后进行后期处理...这些集合具有Count属性,我们可以利用它。

3)根据对象在循环中的顺序为其设置属性...尽管在将对象添加到集合中时可以很容易地对其进行设置。

回答

" foreach"用于迭代实现" IEnumerable"的集合。它通过在集合上调用GetEnumerator来实现,它将返回一个Enumerator

此枚举器具有一个方法和一个属性:

  • MoveNext()
  • 当前的

Current返回枚举器当前所在的对象,MoveNext将Current更新为下一个对象。

显然,索引的概念与枚举的概念是陌生的,无法做到。

因此,大多数集合都可以使用索引器和for循环构造遍历。

与使用局部变量跟踪索引相比,在这种情况下,我非常喜欢使用for循环。

回答

可以做这样的事情:

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

回答

它仅适用于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();

@乔纳森我没有说这是一个很好的答案,我只是说这只是表明有可能做到他所要求的:)

@Graphain我不希望它很快,但我不确定它是如何工作的,它每次可以在整个列表中重复以找到匹配的对象,这将是一个比较对象。

也就是说,List可以保留每个对象的索引以及计数。

乔纳森(Jonathan)似乎有更好的主意,如果他能详细说明?

最好只统计一下我们要达到的目标,而且更简单,适应性更强。

回答

字面答案-警告,性能可能不如仅使用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);
}

回答

我不同意" for"循环在大多数情况下是更好的选择的意见。

" foreach"是一个有用的构造,在任何情况下都不能被" for"循环替代。

例如,如果我们有一个DataReader并使用" foreach"循环遍历所有记录,它将自动调用Dispose方法并关闭阅读器(然后可以自动关闭连接)。因此,这样做更安全,因为即使我们忘记关闭阅读器,它也可以防止连接泄漏。

(确保总是关闭读者是一个好习惯,但是如果我们不这样做,编译器将不会捕获它,我们不能保证已经关闭了所有读者,但是可以通过进入来使它更有可能不会泄漏连接使用foreach的习惯。)

可能还有其他一些例子,其中隐式调用Dispose方法很有用。

回答

这是我刚想出的解决方案

原始代码:

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

回答

int index;
foreach (Object o in collection)
{
    index = collection.indexOf(o);
}

这将适用于支持IList的集合。

回答

我们可以将原始枚举数与另一个包含索引信息的枚举数包装在一起。

foreach (var item in ForEachHelper.WithIndex(collection))
{
    Console.Write("Index=" + item.Index);
    Console.Write(";Value= " + item.Value);
    Console.Write(";IsLast=" + item.IsLast);
    Console.WriteLine();
}

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

回答

最好像这样使用关键字continue安全构造

int i=-1;
foreach (Object o in collection)
{
    ++i;
    //...
    continue; //<--- safe to call, index will be increased
    //...
}

回答

这就是我的操作方式,这很简单/简洁,但是如果我们在循环体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);
    ...
}

回答

我对这个问题的解决方案是扩展方法WithIndex()

http://code.google.com/p/ub-dotnet-utilities/source/browse/trunk/Src/Utilities/Extensions/EnumerableExtensions.cs

像这样使用

var list = new List<int> { 1, 2, 3, 4, 5, 6 };    

var odd = list.WithIndex().Where(i => (i.Item & 1) == 1);
CollectionAssert.AreEqual(new[] { 0, 2, 4 }, odd.Select(i => i.Index));
CollectionAssert.AreEqual(new[] { 1, 3, 5 }, odd.Select(i => i.Item));

回答

这样的事情怎么样?请注意,如果myEnumerable为空,则myDelimitedString可以为null。

IEnumerator enumerator = myEnumerable.GetEnumerator();
string myDelimitedString;
string current = null;

if( enumerator.MoveNext() )
    current = (string)enumerator.Current;

while( null != current)
{
    current = (string)enumerator.Current; }

    myDelimitedString += current;

    if( enumerator.MoveNext() )
        myDelimitedString += DELIMITER;
    else
        break;
}