代码之家  ›  专栏  ›  技术社区  ›  lomaxx

我什么时候应该使用IEnumerator在C中循环?

  •  24
  • lomaxx  · 技术社区  · 16 年前

    我想知道在foreach循环上使用IEnumerator迭代集合是否有什么好处?例如,在任何时候,使用以下代码示例中的任何一个都比另一个更好吗?

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

    而不是

    foreach (var class in myClasses)
        Console.WriteLine(class);
    
    4 回复  |  直到 16 年前
        1
  •  31
  •   Community CDub    7 年前

    首先,请注意,您的示例中有一个很大的区别(在 foreach GetEnumerator 是吗? 前额 认购保证书 Dispose() 如果迭代器是 IDisposable . 这对许多迭代器很重要(例如,它可能使用外部数据馈送)。

    实际上,有些情况下 前额 没有我们想要的那样有用。

    首先,讨论“第一项”案例 here (foreach / detecting first iteration) .

    但更多;如果你试着写失踪的 Zip 将两个可枚举序列缝合在一起的方法(或 SequenceEqual 方法),你发现你可以使用 前额 在这两个序列上,因为这将执行交叉联接。您需要直接对其中一个使用迭代器:

    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            
    }
    
        2
  •  19
  •   Jay Bazuzi Buck Hodges    16 年前

    根据 C# language spec :

    窗体的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 .

        3
  •  12
  •   Jay Bazuzi Buck Hodges    16 年前

    在C, foreach 做的事情和 IEnumerator 上面发布的代码。这个 前额 提供的构造使程序员更容易使用。我相信在这两种情况下产生的IL是相同/相似的。

        4
  •  4
  •   Chaowlert Chaisrichalermpol    16 年前

    当第一次迭代与其他迭代不同时,我有时会使用它。

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

    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
    

    另一种情况是,我将两个枚举器迭代在一起。

    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