24

我想知道是否有任何时候在 foreach 循环上使用 IEnumerator 来遍历集合是有利的?例如,是否有任何时候使用以下代码示例中的任何一个比另一个更好?

IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator();
while(classesEnum.MoveNext())
    Console.WriteLine(classesEnum.Current);

代替

foreach (var class in myClasses)
    Console.WriteLine(class);
4

4 回答 4

31

首先,请注意,您的示例(介于foreach和之间GetEnumerator)的一个很大区别是,如果迭代器是 ,则foreach保证调用Dispose()迭代器IDisposable。这对于许多迭代器很重要(例如,它们可能正在使用外部数据馈送)。

实际上,有些情况并foreach没有我们想要的那么有用。

首先,这里讨论了“第一项”情况(foreach / detection first iteration)

还有一点; 如果您尝试编写将Zip两个可枚举序列拼接在一起的缺失方法(或SequenceEqual方法),您会发现您可以foreach在两个序列上使用,因为这将执行交叉连接。您需要直接将迭代器用于其中之一:

static IEnumerable<T> Zip<T>(this IEnumerable<T> left,
    IEnumerable<T> right)
{
    using (var iter = right.GetEnumerator())
    {
        // consume everything in the first sequence
        foreach (var item in left)
        {
            yield return item;

            // and add an item from the second sequnce each time (if we can)
            if (iter.MoveNext())
            {
                yield return iter.Current;
            }
        }
        // any remaining items in the second sequence
        while (iter.MoveNext())
        {
            yield return iter.Current;
        }                
    }            
}

static bool SequenceEqual<T>(this IEnumerable<T> left,
    IEnumerable<T> right)
{
    var comparer = EqualityComparer<T>.Default;

    using (var iter = right.GetEnumerator())
    {
        foreach (var item in left)
        {
            if (!iter.MoveNext()) return false; // first is longer
            if (!comparer.Equals(item, iter.Current))
                return false; // item different
        }
        if (iter.MoveNext()) return false; // second is longer
    }
    return true; // same length, all equal            
}
于 2009-01-19T05:07:43.197 回答
19

根据C# 语言规范

形式的 foreach 语句

foreach (V v in x) embedded-statement

然后扩展为:

{
    E e = ((C)(x)).GetEnumerator();
    try {
            V v;
            while (e.MoveNext()) {
                    v = (V)(T)e.Current;
                    embedded-statement
            }
    }
    finally {
            ... // Dispose e
    }
}

你的两个例子的本质是一样的,但是有一些重要的区别,最明显的是try/ finally

于 2009-01-19T04:01:22.907 回答
12

在 C# 中,所做的事情与您在上面发布的代码foreach完全相同。IEnumerator提供该foreach构造是为了使程序员更容易。我相信在这两种情况下生成的 IL 是相同/相似的。

于 2009-01-19T03:20:53.833 回答
4

我有时会在第一次迭代做一些与其他人不同的事情时使用它。

例如,如果要将成员打印到控制台并按行分隔结果,则可以编写。

using (IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator()) {
    if (classEnum.MoveNext())
        Console.WriteLine(classesEnum.Current);
    while (classesEnum.MoveNext()) {
        Console.WriteLine("-----------------");
        Console.WriteLine(classesEnum.Current);
    }
}

然后结果是

myClass 1
-----------------
myClass 2
-----------------
myClass 3

另一种情况是当我一起迭代 2 个枚举器时。

using (IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator()) {
    using (IEnumerator<MyClass> classesEnum2 = myClasses2.GetEnumerator()) {
        while (classesEnum.MoveNext() && classEnum2.MoveNext())
            Console.WriteLine("{0}, {1}", classesEnum.Current, classesEnum2.Current);
    }
}

结果

myClass 1, myClass A
myClass 2, myClass B
myClass 3, myClass C

使用枚举器可以让您在迭代中更加灵活。但在大多数情况下,您可以使用 foreach

于 2009-01-19T04:42:35.617 回答