迭代器和枚举器之间的区别
一个.NET 3.5作业的面试问题是“迭代器和枚举器有什么区别”?
这是一个核心区别,用LINQ来做什么等等。
无论如何,有什么区别? 我似乎无法在网上find一个可靠的定义。 没有错,我可以find两个术语的含义,但我得到的答案略有不同。 面试的最佳答案是什么?
IMO一个迭代器“迭代”一个集合,一个枚举器提供迭代的function,但是这个必须被调用。
而且,使用yield关键字被认为是保存状态。 这个状态到底是什么? 有没有这种好处发生的例子?
迭代意味着重复一些步骤,而枚举意味着要经历值集合中的所有值。 所以枚举通常需要某种forms的迭代。
这样,枚举就是迭代的一个特例,其中的步骤是从集合中获取值。
请注意,“通常” – 枚举也可以recursion执行,但recursion和迭代是如此密切相关,我不会在乎这个小的差异。
您也可以枚举您未明确存储在集合中的值。 例如,你可以枚举自然数,素数或其他,但是你可以在枚举过程中计算这些值,而不是从物理集合中检索它们。 你理解这种情况是枚举一个虚拟集合,它的值由一些逻辑定义。
我认为里德·科普赛明白了这一点。 在C#中有两种主要的枚举方法。
- 实现
Enumerable
和一个实现IEnumerator
的类 - 用
yield
语句实现一个迭代器
第一种方法很难实现,并使用对象进行枚举。 第二种方式更容易实现并使用延续。
在C#2+中, 迭代器是编译器为您自动生成IEnumerable和/或IEnumerable <T>接口的一种方式。
如果没有迭代器,您将需要创build一个实现IEnumerator的类,包括Current,MoveNext和Reset。 这需要相当数量的工作。 通常,您将创build一个私有类,为您的types实现IEnumerator <T>,然后yourClass.GetEnumerator()将构造该私有类并返回它。
迭代器是编译器使用简单的语法(yield)为你自动生成的一种方法。 这使您可以直接在您的类中实现GetEnumerator(),而无需指定第二个类(IEnumerator)。 这个阶级的所有成员都是为你而build造的。
迭代器对开发人员非常友好 – 事情是以非常有效的方式完成的,而且工作量less得多。
当你使用foreach时,两者的行为是相同的(只要你正确地编写自定义的IEnumerator)。 迭代器只是让生活变得更简单。
C#调用迭代器更常见(在C#世界之外),称为生成器或生成器函数 (例如,在Python中)。 生成器函数是协程的一个特例。 AC#iterator(generator)是一个枚举器 (实现IEnumerable
接口的数据types)的特殊forms。
我不喜欢C#生成器这个术语迭代器的用法,因为它和迭代器一样是一个枚举器。 微软不能改变主意了。
相比之下,考虑到在C ++中,迭代器是一个主要用于访问集合中顺序元素的值。 它可以被提前,取消一个值,并testing是否已经达到集合的末尾。
为了理解迭代器,我们首先需要了解枚举器。
调查员是专门的对象,它提供了一种一次一个地移动一个有序的项目列表的方法(同一种东西有时被称为“光标”)。 .NET框架提供了两个与枚举器相关的重要接口:IEnumerator和IEnumerable。 实现IEnumerator的对象本身就是枚举器; 他们支持以下成员:
-
属性Current,它指向列表中的一个位置
-
MoveNext方法,它沿着列表移动当前项目
-
方法Reset(重置),将当前项目移动到其初始位置(位于第一个项目之前)。
另一方面,旅行者执行enumerattor ptern。 .NET 2.0引入了一个编译器,它是一个编译器。 当可以枚举的对象直接或间接地获取GetEnumerator时,编译器生成并返回一个特定的对象。 可选地,提交者可以是可以被整理的数字和数字对象。
限幅器的基本要素是收益率。 旅行者和消费者之间有一个很大的区别:旅行者不执行重置方法。 在redirect器上复位重置方法是一个例外。
迭代器的意义在于允许枚举器的简单实现。 如果一个方法需要为一个有序列表项返回一个枚举器或一个可枚举类,那么它就会被写入,以便使用“yield”语句以正确的顺序返回每个项目。
“虽然foreach语句是枚举器的使用者,但迭代器是枚举器的生产者。”
以上是“C#5.0在NutShell”中的解释,对我有帮助。
换句话说,foreach语句使用MoveNext()和IEnumerator的Current属性遍历序列,而迭代器用于生成将由foreach语句使用的IEnumerator的实现。 在C#中,当你编写一个包含yield语句的迭代器方法时,编译器会为你生成一个私有枚举器。 当你迭代序列中的项目时,它将调用私有枚举器的MoveNext()和Current属性。 这些方法/属性是通过迭代器方法中的代码实现的,它将被重复调用以产生值,直到没有剩余的值产生为止。
这是我对C#如何定义枚举器和迭代器的理解。
由于没有例子,这是一个对我有帮助的。
枚举器是您在实现IEnumerator接口的类或types上调用.GetEnumerator()时所获得的对象。 当这个接口被实现的时候,你已经创build了编译器所需的所有代码,使你能够使用foreach
来“迭代”你的集合。
枚举器和迭代器都允许“迭代”,枚举和迭代基本上是相同的过程,但实现方式不同,枚举意味着你已经实现了IEnumerator接口。迭代意味着你已经在你的类中创build了迭代器构造(在下面展示),并且在你的类上调用了foreach
,这时编译器会为你自动创build枚举器的function。
另外请注意,您不必与您的统计员蹲坐。 你可以整天调用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
“迭代器是C#2.0中的一个新特性,迭代器是一种方法,获取访问器或运算符,使您无需实现整个IEnumerable接口就可以支持对类或结构中的foreach迭代,而是只提供一个迭代器,只需遍历类中的数据结构,当编译器检测到迭代器时,它将自动生成IEnumerable或IEnumerable接口的Current,MoveNext和Dispose方法。 – msdn
枚举处理对象,而迭代仅处理值。 当我们使用向量散列表等时使用枚举,while循环等循环中使用迭代等。我从来没有使用yield关键字,所以我不能告诉你。
- OpenWrap vs NuGet
- 为什么closures使用AllocConsole启动的控制台会导致我的整个应用程序退出? 我可以改变这种行为吗?
- 以编程方式获取C#堆栈跟踪
- IEnumerable Count()和Length之间的区别
- 无法更新EntitySet – 因为它有一个DefiningQuery并且不存在<UpdateFunction>元素
- 绑定到值的可编辑ComboBox不在列表中
- .NET Framework,CLR和Visual Studio版本号如何相互关联?
- 在使用System.DirectoryServices时尝试访问卸载的AppDomain
- 为可变对象重写GetHashCode?