7

我想知道为什么 GetEnumerator() 方法是从 IEnumerator 中提取出来并放在 IEnumerable 中的。在我看来,将所有枚举器方法保留在 IEnumerator 中会更有意义。

谢谢,

斯科特

4

6 回答 6

19

问问自己“想象一下这是不是真的”。

如果所有的枚举方法都在一个接口上,两个调用者怎么可能同时枚举同一个列表呢?

有两个接口,因为一个说,“你可以枚举我”,而另一个说,“这是一个跟踪给定枚举任务的对象。”

IEnumerable接口是一个工厂,可以根据需要创建任意数量的对象IEnumerator。这些枚举器如何以及何时使用取决于消费者。

于 2010-01-02T22:51:36.447 回答
7

IEnumerable意味着对象是可以以线性方式迭代的数据的集合或源。IEnumerator是执行迭代的实际实现的接口。

于 2010-01-02T22:46:31.137 回答
4

因为“IEnumerable”说“来吧,枚举我”(然后你说——如何,给我枚举器),但是“IEnumerator”说“我可以枚举你的集合!” 而且你已经拥有了,你不需要再得到了。

于 2010-01-02T22:47:59.327 回答
2

你在这里得到了很好的答案。只是强调一点。枚举器保持状态,它跟踪正在枚举的集合中的当前对象。可通过 IEnumerator.Current 获得。它知道如何改变那个状态,IEnumerator.MoveNext()。保持状态需要一个单独的对象来存储状态。该状态不能轻易地存储在集合对象中,因为只有一个集合对象,但可以有多个枚举器。

我使用了“容易”这个短语,因为集合实际上可以跟踪它的枚举数。毕竟,是对集合类的 GetEnumerator() 方法的调用返回了迭代器。.NET 框架中有一个集合类,Microsoft.VisualBasic.Collection。它需要为其 Collection 类实现一个 VB6 合同,并且该合同指定在枚举时更改集合是合法的。这意味着当 Collection 被修改时,它需要对所有创建的迭代器对象进行一些合理的处理。

他们想出了一个很好的技巧,WeakReference 可能是受此启发的。看看Reflector显示的代码。鼓舞人心的东西。再挖掘一些并在集合类中找到“版本”。绝妙的把戏。

于 2010-01-02T23:51:08.573 回答
0

因为通常进行枚举的事物仅与被枚举的事物相切(如果有的话)相关。如果 IEnumerable 和 IEnumerator 是同一个接口,则它们不能共享,或者您需要在 GetEnumerator 上有一些无类型参数,以允许您传入要枚举的对象。拆分它们允许枚举基础结构可能在不同类型之间共享。

于 2010-01-02T22:48:03.380 回答
0

在阅读了 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>());
}
于 2010-01-02T23:54:03.827 回答