迭代器与枚举器之间的区别


77

.NET 3.5作业的面试问题是“迭代器和枚举器有什么区别”?

这是要做出的核心区分,对于LINQ等等。

无论如何,有什么区别?我似乎在网上找不到可靠的定义。没错,我可以找到两个术语的含义,但是得到的答案略有不同。面试的最佳答案是什么?

IMO迭代器在集合上“迭代”,并且枚举器提供了迭代功能,但这必须调用。

另外,据说使用yield关键字可以保存状态。这个状态到底是什么?有这种好处发生的例子吗?

Answers:


56

迭代意味着重复一些步骤,而枚举意味着遍历值集合中的所有值。因此,枚举通常需要某种形式的迭代。

这样,枚举是迭代步骤的特殊情况,步骤是从集合中获取值。

注意“通常” –枚举也可以递归执行,但是递归和迭代是如此紧密相关,以至于我不会在意这个小的差异。

您还可以枚举没有显式存储在集合中的值。例如,您可以枚举自然数,素数或其他任何东西,但是您将在枚举期间计算这些值,而不从物理集合中检索它们。您将这种情况理解为枚举虚拟集合,其值由某些逻辑定义。


我认为里德·科普西(Reed Copsey)明白了这一点。在C#中,有两种主要的枚举方法。

  1. 实施Enumerable和类实施IEnumerator
  2. 使用该yield语句实现迭代器

第一种方法更难实现,并使用对象进行枚举。第二种方法更易于实现并使用延续。


6
但是,在C#中,迭代器是一个特定的特殊结构,而不仅仅是描述该术语的动词。枚举器也是一个特定的接口。与普通的OO术语相比,两者在C#中的含义明显不同。
Reed Copsey

2
哦,是的,你是对的。用我的话来说,这都是枚举,而我完全忘记了C#称为该模式迭代器。
DanielBrückner09年

在C#中,迭代器是一种返回枚举器(看到时为编译器魔术yield)的方法,而枚举器是使用Current和逐一传递值的对象MoveNext()(请参见Ping的答案)。Python中的措辞有所不同,Python的生成器类似于C#枚举器(使用即时创建的值yield),而旧的迭代器也具有相同的功能,但是没有yield(因此没有暂停机制,因此无法使用循环)。
分钟

46

在C#2+中,迭代器是编译器自动为您生成IEnumerable和/或IEnumerable <T>接口的一种方式。

如果没有迭代器,则需要创建一个实现IEnumerator的类,包括Current,MoveNext和Reset。这需要大量的工作。通常,您将创建一个私有类,该私有类会为您的类型隐含IEnumerator <T>,然后yourClass.GetEnumerator()将构造该私有类并返回它。

迭代器是编译器使用一种简单的语法(收益)自动为您生成此代码的方法。这使您可以直接在您的类中实现GetEnumerator(),而无需您指定第二个类(IEnumerator)。该类及其所有成员的构造已为您完成。

迭代器对开发人员非常友好-事情以非常有效的方式完成,而工作量却少得多。

使用foreach时,两者的行为将相同(前提是您正确编写了自定义IEnumerator)。迭代器使生活变得简单得多。


22

C#所谓的迭代器更常见(在C#领域之外)称为生成器生成器函数(例如,在Python中)。生成函数是协程的一种特殊情况。AC#迭代器(生成器)是枚举器(实现IEnumerable接口的数据类型)的一种特殊形式。

我不喜欢C#生成器这种术语“迭代器”的用法,因为它既是枚举器,又是迭代器。微软改变主意为时已晚。

为了对比,请考虑在C ++中,迭代器是一个值,该值主要用于访问集合中的顺序元素。可以对其进行高级处理,推断出获取值,并进行测试以查看是否已达到集合的末尾。


2
好吧,这是最好的答案;)
mrzepa 2015年

13

“虽然foreach语句是枚举器的使用者,但迭代器是枚举器的使用者。”

以上是“ C#5.0在一个NutShell中”的解释方式,对我有所帮助。

换句话说,foreach语句使用MoveNext()和IEnumerator的Current属性来遍历序列,而迭代器用于生成将被foreach语句使用的IEnumerator的实现。在C#中,当您编写包含yield语句的迭代器方法时,编译器将为您生成一个私有枚举器。当您遍历序列中的项目时,它将调用私有枚举器的MoveNext()和Current属性。这些方法/属性由您在迭代器方法中的代码实现,将被重复调用以产生值,直到没有剩余值可产生为止。

这是我对C#如何定义枚举数和迭代器的理解。


1
那句话非常简洁地总结了一下。太可惜了,这个答案太远了,让我+1!
AnorZaken

12

要了解迭代器,我们首先需要了解枚举器。

枚举器是一种特殊的对象,它使人们能够一次浏览一件物品的有序列表(同一类物品有时称为“光标”)。.NET框架提供了两个与枚举器有关的重要接口:IEnumerator和IEnumerable。实现IEnumerator的对象本身就是枚举器;他们支持以下成员:

  • 属性Current,它指向列表中的位置

  • MoveNext方法,该方法将当前项沿列表移动一

  • Reset方法,将Current项目移动到其初始位置(在第一个项目之前)。

另一方面,迭代器实现枚举器模式。.NET 2.0引入了迭代器,该迭代器是编译器枚举的枚举器。当可枚举对象直接或间接调用GetEnumertor时,编译器将生成并返回一个适当的迭代器对象。可选地,迭代器可以是枚举对象和枚举对象的组合。

迭代器块的重要组成部分是产量策略。迭代器和枚举器之间有一个很大的区别:迭代器不实现Reset方法。在迭代器上调用Reset方法会导致异常。

迭代器的目的是允许轻松实现枚举器。如果方法需要为项目的有序列表返回枚举器或可枚举的类,则将其编写为使用“ yield”语句按正确的顺序返回每个项目。


8

由于没有给出示例,因此这对我很有帮助。

枚举数是在实现IEnumerator接口的类或类型上调用.GetEnumerator()时获得的对象。实施此接口后,您已经创建了编译器所需的所有代码,以使您foreach能够“迭代”整个集合。

不过不要把“ iterate”一词与迭代器混淆了,Enumerator和Iterator都允许您“迭代”。枚举和迭代基本上是相同的过程,但是实现方式不同。枚举意味着您已经实现了IEnumerator接口。迭代意味着您已经在类中创建了迭代器构造(如下所示),并且您正在调用foreach类,这时编译器会自动为您创建枚举器功能。

还要注意,您不必蹲下枚举器。您可以MyClass.GetEnumerator()整天打电话,而不做任何事(例如:

IEnumerator myEnumeratorThatIWillDoNothingWith = MyClass.GetEnumerator())。

还要注意,您的类中的迭代器构造只有在实际使用它时才被使用,即您在类上调用过foreach

这是msdn的迭代器示例:

public class DaysOfTheWeek : System.Collections.IEnumerable
{

     string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

     //This is the iterator!!!
     public System.Collections.IEnumerator GetEnumerator()
     {
         for (int i = 0; i < days.Length; i++)
         {
             yield return days[i];
         }
     }

}

class TestDaysOfTheWeek
{
    static void Main()
    {
        // Create an instance of the collection class
        DaysOfTheWeek week = new DaysOfTheWeek();

        // Iterate with foreach - this is using the iterator!!! When the compiler
        //detects your iterator, it will automatically generate the Current, 
        //MoveNext and Dispose methods of the IEnumerator or IEnumerator<T> interface
        foreach (string day in week)
        {
            System.Console.Write(day + " ");
        }
    }
}
// Output: Sun Mon Tue Wed Thr Fri Sat

4

“迭代器是C#2.0中的新功能。迭代器是一种方法,获取访问器或运算符,它使您能够支持类或结构中的每个迭代,而无需实现整个IEnumerable接口。相反,您只需提供一个迭代器即可,只需遍历类中的数据结构。当编译器检测到迭代器时,它将自动生成IEnumerable或IEnumerable接口的Current,MoveNext和Dispose方法。” - MSDN


2

枚举处理对象,而迭代仅处理值。当我们使用向量哈希表等时使用枚举,而在while循环中使用迭代时使用枚举。我从未使用过yield关键字,所以我无法告诉您。


这可能是正确的,但仅适用于点网。在某些语言中,您可以迭代对象/值并枚举对象/值
Tim Matthews

我是.NET程序员。很抱歉,我的无知就是我了解他们的工作方式。
Kredns

0

迭代处理数组字符串,而迭代处理对象

在JavaScript中,您可以使用以下方法迭代数组或字符串:

  • 每个循环
  • for循环
  • 对于循环
  • 循环
  • while循环

您可以使用以下方法枚举对象:

  • 用于循环
  • Object.keys()方法
  • Object.values()方法
  • Object.entries()方法
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.