我想知道为什么 GetEnumerator() 方法是从 IEnumerator 中提取出来并放在 IEnumerable 中的。在我看来,将所有枚举器方法保留在 IEnumerator 中会更有意义。
谢谢,
斯科特
我想知道为什么 GetEnumerator() 方法是从 IEnumerator 中提取出来并放在 IEnumerable 中的。在我看来,将所有枚举器方法保留在 IEnumerator 中会更有意义。
谢谢,
斯科特
问问自己“想象一下这是不是真的”。
如果所有的枚举方法都在一个接口上,两个调用者怎么可能同时枚举同一个列表呢?
有两个接口,因为一个说,“你可以枚举我”,而另一个说,“这是一个跟踪给定枚举任务的对象。”
IEnumerable
接口是一个工厂,可以根据需要创建任意数量的对象IEnumerator
。这些枚举器如何以及何时使用取决于消费者。
IEnumerable
意味着对象是可以以线性方式迭代的数据的集合或源。IEnumerator
是执行迭代的实际实现的接口。
因为“IEnumerable”说“来吧,枚举我”(然后你说——如何,给我枚举器),但是“IEnumerator”说“我可以枚举你的集合!” 而且你已经拥有了,你不需要再得到了。
你在这里得到了很好的答案。只是强调一点。枚举器保持状态,它跟踪正在枚举的集合中的当前对象。可通过 IEnumerator.Current 获得。它知道如何改变那个状态,IEnumerator.MoveNext()。保持状态需要一个单独的对象来存储状态。该状态不能轻易地存储在集合对象中,因为只有一个集合对象,但可以有多个枚举器。
我使用了“容易”这个短语,因为集合实际上可以跟踪它的枚举数。毕竟,是对集合类的 GetEnumerator() 方法的调用返回了迭代器。.NET 框架中有一个集合类,Microsoft.VisualBasic.Collection。它需要为其 Collection 类实现一个 VB6 合同,并且该合同指定在枚举时更改集合是合法的。这意味着当 Collection 被修改时,它需要对所有创建的迭代器对象进行一些合理的处理。
他们想出了一个很好的技巧,WeakReference 可能是受此启发的。看看Reflector显示的代码。鼓舞人心的东西。再挖掘一些并在集合类中找到“版本”。绝妙的把戏。
因为通常进行枚举的事物仅与被枚举的事物相切(如果有的话)相关。如果 IEnumerable 和 IEnumerator 是同一个接口,则它们不能共享,或者您需要在 GetEnumerator 上有一些无类型参数,以允许您传入要枚举的对象。拆分它们允许枚举基础结构可能在不同类型之间共享。
在阅读了 Bart De Smet 在minlinq上的帖子后,我不再认为严格要求拆分两个接口。
例如 - 在上面的链接中,IEnumerable/IEnumerator 被折叠到单个方法接口
Func<Func<Option<T>>>
和一些基本的实现
public static class FEnumerable
{
public static Func<Func<Option<T>>> Empty<T>()
{
return () => () => new Option<T>.None();
}
public static Func<Func<Option<T>>> Return<T>(T value)
{
return () =>
{
int i = 0;
return () =>
i++ == 0
? (Option<T>)new Option<T>.Some(value)
: (Option<T>)new Option<T>.None();
};
}
...
}
public static Func<Func<Option<T>>> Where<T>(this Func<Func<Option<T>>> source, Func<T, bool> filter)
{
return source.Bind(t => filter(t) ? FEnumerable.Return(t) : FEnumerable.Empty<T>());
}