80

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

这是一个核心区别,LINQ 等等。

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

IMO 一个迭代器“迭代”一个集合,而一个枚举器提供了迭代的功能,但这必须被调用。

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

4

9 回答 9

61

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

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

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

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


我认为 Reed Copsey 明白了这一点。在 C# 中有两种主要的枚举方式。

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

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

于 2009-04-04T01:16:45.157 回答
47

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

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

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

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

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

于 2009-04-04T01:18:22.797 回答
22

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

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

相比之下,在 C++ 中,迭代器是一个值,主要用于访问集合中的顺序元素。可以对其进行高级、取消引用以检索值,并对其进行测试以查看是否已到达集合的末尾。

于 2009-10-27T11:50:48.387 回答
16

“而 foreach 语句是枚举器的消费者,而迭代器是枚举器的生产者。”

以上是“C# 5.0 In A NutShell”的解释,对我很有帮助。

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

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

于 2014-07-23T01:28:12.960 回答
12

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

枚举器是专门的对象,它为人们提供了一次通过一个有序的项目列表的方法(同一类型的东西有时称为“光标”)。.NET 框架提供了两个与枚举器相关的重要接口:IEnumerator 和 IEnumerable。实现 IEnumerator 的对象本身就是枚举器;他们支持以下成员:

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

  • 方法 MoveNext,它将当前项目沿列表移动一个

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

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

迭代器块的基本组成部分是 yield 语句。迭代器和枚举器之间有一个很大的区别:迭代器不实现 Reset 方法。在迭代器上调用 Reset 方法会导致异常。

迭代器的重点是允许简单地实现枚举器。如果一个方法需要返回一个枚举数或一个可枚举类来返回一个有序的项目列表,它被编写成使用'yield'语句以正确的顺序返回每个项目。

于 2009-04-04T01:25:23.687 回答
9

由于没有给出例子,这里有一个对我有帮助的例子。

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

不要把“迭代”这个词与迭代器混淆。枚举器和迭代器都允许你“迭代”。枚举和迭代基本上是相同的过程,但实现方式不同。枚举意味着你已经实现了 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
于 2011-02-02T18:28:42.847 回答
4

“迭代器是 C# 2.0 中的一项新功能。迭代器是一种方法、get 访问器或运算符,使您能够支持类或结构中的 foreach 迭代,而无需实现整个 IEnumerable 接口。相反,您只提供一个迭代器,它简单地遍历你类中的数据结构,当编译器检测到你的迭代器时,它会自动生成IEnumerable或IEnumerable接口的Current、MoveNext和Dispose方法。-msdn _

于 2009-04-04T01:12:30.003 回答
2

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

于 2009-04-04T01:11:34.437 回答
0

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

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

  • forEach循环
  • for循环
  • for循环
  • 做while循环
  • while循环

你可以枚举一个对象:

  • for in循环
  • Object.keys()方法
  • Object.values()方法
  • Object.entries()方法
于 2020-06-27T19:21:00.687 回答