关于 IEnumerable 和 IEnumerator 的问题
我使用以下代码使 myClass 能够使用 foreach。但我对编程相当陌生,并且在理解以下代码时遇到一些困难。我在评论中描述了我的问题。如果您提供一些信息,我将不胜感激。
public class MyClass : IEnumerable<string>
{
//1) What is IEnumerator for?
// Whats the difference between IEnumerator and IEnumerable
public IEnumerator<string> GetEnumerator()
{
yield return "first";
yield return "second";
}
//2) What is it for? It just calls above method
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
//3) Lastly what benefits I have from implementing genetic interface
//IEnumerable<string> instead of just IEnumerable
I use the following code to enable myClass to use foreach. But I am rather new to programming and have some difficulty in understanding the following code. I described my problems in the comments. I would be grateful for providing some information.
public class MyClass : IEnumerable<string>
{
//1) What is IEnumerator for?
// Whats the difference between IEnumerator and IEnumerable
public IEnumerator<string> GetEnumerator()
{
yield return "first";
yield return "second";
}
//2) What is it for? It just calls above method
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
//3) Lastly what benefits I have from implementing genetic interface
//IEnumerable<string> instead of just IEnumerable
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
杰森的回答很好,但我想我应该补充一下我对此的看法。想象你有一个序列:
现在想象你有一个箭头指向该序列的某个位置:
“箭头”是一个可以做两件事的对象。首先,它可以给你它所指向的东西。其次,它可以使自己指向下一件事。
IEnumerator 是一个箭头。它有一个属性 Current,可以为您提供它所指向的东西。它有一个方法,MoveNext(),它使自己指向下一个事物。
首先你如何获得箭头?你需要一个箭厂。你向工厂请求一个箭头,它给你一个指向序列中第一个元素的箭头。
IEnumerable 是一个箭头工厂。它有一个方法 GetEnumerator,可以为您提供指向序列第一个元素的箭头。
该方案的一个很好的特性是您可以在同一序列中使用多个箭头指向不同的位置。
假设序列是整数。如果您实现 IEnumerable ,那么当您说
实际执行的操作是将序列中的 int 转换为对象,将整数装箱,然后立即将对象拆箱回整数,从而添加完全不必要的内存分配到每一个操作。如果编译器知道该序列是整数,那么它可以跳过不必要的装箱操作。
假设序列是字符串。如果您实现了
IEnumerable
那么您可以说:如果您没有实现,那么您必须说出
哪个是不必要的且容易出错。您可以使用类型系统简单地保证类型为字符串,而不是通过强制转换来指示编译器类型为字符串。
Jason's answer is good but I thought I'd just add how I think about this. Imagine you have a sequence:
Now imagine you have an arrow pointing at some position of that sequence:
An "arrow" is an object that can do two things. First, it can give you the thing it is pointing at. Second, it can make itself point at the next thing.
IEnumerator is an arrow. It has a property, Current, that gives you the thing it is pointing at. It has a method, MoveNext() that makes itself point at the next thing.
How do you get an arrow in the first place? You need an arrow factory. You ask the factory for an arrow, and it gives you an arrow that points to the first element in the sequence.
IEnumerable is an arrow factory. It has a method, GetEnumerator, that gives you an arrow to the first element of the sequence.
A nice property of this scheme is that you can have multiple arrows pointing to different places in the same sequence.
Suppose the sequence is of integers. If you implement
IEnumerable
then when you saywhat that will actually do is convert the int in the sequence to object, boxing the integer, and then immediately unbox the object back to integer, adding a completely unnecessary memory allocation to every single operation. If the compiler knows that the sequence is of integers then it can skip the unnecessary boxing operation.
Suppose the sequence is of strings. If you implement
IEnumerable<string>
then you can say:If you don't, then you have to say
which is unnecessary and error-prone. Rather than instruct the compiler via a cast that the type is string, you can simply guarantee that the type is string by using the type system.
IEnumerator
是一个接口,它表示可让您枚举序列的方法。IEnumerator
和IEnumerable
之间的区别在于,前者表示可枚举序列的对象的约定,而后者表示可枚举序列的对象的约定。被一一列举。前者表示合约
IEnumerable
上方法GetEnumerator
的实现。后者表示合约IEnumerable
上方法GetEnumerator
的显式实现。问题在于,两个合约都有一个名为GetEnumerator
的方法,但返回类型不同,因此一个方法无法同时满足两个合约(任何实现IEnumerable
的类都必须还将IEnumerable
实现为IEnumerable: IEnumerable
)。后者调用IEnumerable.GetEnumerator
的实现,因为这是一个明智的实现,它将IEnumerator
返回为IEnumerator。 :IEnumerator
。强打字。您知道序列
IEnumerable
中的元素都是String
的实例,但您不知道对于IEnumerable
并且可能会结束up 尝试将后者的元素强制转换为String
的实例,但事实并非如此。IEnumerator
is an interface that represents methods that let you enumerate a sequence. The difference betweenIEnumerator
andIEnumerable
is that the former represents the contract for objects that let you enumerate a sequence, and the latter represents the contract for objects that are a sequence that can be enumerated over.The former represents an implementation of the method
GetEnumerator
on the contractIEnumerable<string>
. The latter represents an explicit implementation of the methodGetEnumerator
on the contractIEnumerable
. The issue is that both contracts have a method namedGetEnumerator
but with different return types so that a method can't simultaneously satisfy both contracts (any class implementingIEnumerable<T>
must also implementIEnumerable
asIEnumerable<T> : IEnumerable
). The latter invokes the implementation ofIEnumerable<string>.GetEnumerator
as that is a sensible implementation that returns anIEnumerator
asIEnumerator<string> : IEnumerator
.Strong typing. You know that the elements in a sequence
IEnumerable<string>
are all instances ofString
whereas you don't know that forIEnumerable
and could end up trying to cast an element of the latter to an instance ofString
when it can not be.这是
IEnumerable
的声明:您别无选择,您必须也实现非泛型 IEnumerable 接口。如果省略 IEnumerable.GetEnumerator() 实现,则会出现编译错误。这是 .NET 1.x 时代(泛型尚不可用)和 .NET 2.0 过渡期(需要支持与 .NET 1.x 程序集的互操作)留下的包袱。我们被困住了。
This is the declaration for
IEnumerable<T>
:You have no choice, you have to implement the non-generic IEnumerable interface as well. You'll get a compile error if you omit the IEnumerable.GetEnumerator() implementation. This is baggage left over from the .NET 1.x days where generics weren't available yet and the transition period for .NET 2.0 where it needed to support interop with .NET 1.x assemblies. We're stuck with it.
IEnumerator 是 MoveNext() 和 Current 成员的真正工作部分。
IEnumerable 是集合的接口,用于指示它具有 GetEnumerator()。
非泛型方法只是为了向后兼容。请注意,通过使用显式实现,它会尽可能地“移出视线”。实施它是因为你必须这样做,然后就忘记它。
当与 foreach 一起使用时,优势很小,因为 foreach 将对循环变量进行类型转换。它允许您在 foreach 中使用
var
:但是使用
IEnumerable
您还可以为其他区域提供类型安全的接口,尤其是 LINQ:IEnumerator is the real working part with the MoveNext() and Current members.
IEnumerable is the interface for a collection to signal that it has an GetEnumerator().
The non-generic method is just there for backward compatibility. Note that it is moved 'out of sight' as much as possible by use of the explicit implementation. Implement it because you must and then forget about it.
When used with
foreach
the adavantage is small, as foreach will type-cast the looping variable. It will let you usevar
in the foreach:But with
IEnumerable<string>
you also have a type-safe interface to other areas, most notably LINQ:GetEnumerator
在泛型被引入该语言之前就已经存在了,为了不破坏预先存在的代码,默认方法会调用泛型实现。使用通用枚举,类的使用者在迭代时不需要将每个项目转换为字符串。
GetEnumerator
has been around since before generics were introduced to the language, so as to not break pre-existing code, the default method calls through to the generic implementation.With a generic enumeration, the consumer of your class doesn't need to cast each item to string when iterating.
关于第三个问题(为什么使用泛型?)这里有一些答案:
我们应该使用通用集合来提高安全性和性能吗?
简而言之,尽可能使用通用集合。
Regarding the 3rd question (why use generics?) here are some answers:
Should we use Generic Collection to improve safety and performance?
In short, use generic collections every time you can.
要了解其差异,您需要了解其用途。实际上,它们代表了迭代器设计模式,这是面向对象软件开发人员使用的设计模式之一。
迭代器模式提供了一种顺序访问集合元素的方法,而无需知道集合的结构。
在传统的设计模式方法中,迭代器模式具有用于创建 Iterator 对象的 Aggregate 接口和用于遍历 Aggregate 对象的 Iterator 接口。
现在,IEnumerable 充当保证返回迭代器的聚合接口。迭代器(也称为枚举器)负责生成由特定条件定义的序列中的下一个元素。 IEnumerator 是定义此功能的接口。您可以在我的博客文章 C# 迭代器模式中了解有关此模式的更多信息
To understand the difference you need to understand its use. Actually, they represent an Iterator design pattern which is one of the design patterns used by object-oriented software developers.
The Iterator pattern provides a way of accessing elements of a collection sequentially, without knowing how the collection is structured.
In the traditional design pattern approach, the iterator pattern has an Aggregate interface for creating an Iterator object and the Iterator interface for traversing an Aggregate object.
Now, IEnumerable acts as an Aggregate interface that guarantees to return an iterator. Iterators (also known as Enumerators) are responsible for producing the next element in a sequence defined by certain criteria. IEnumerator is an interface that defines this functionality. You can learn more about this pattern in my blog post Iterator Pattern C#