为什么GetEnumerator()存储在与IEnumerator不同的接口中?

时间:2010-01-02 22:43:50

标签: c# iterator ienumerable ienumerator

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

谢谢,

斯科特

6 个答案:

答案 0 :(得分:18)

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

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

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

IEnumerable界面是一个工厂,可以根据需要创建尽可能多的IEnumerator个对象。这些调查员如何以及何时被使用取决于消费者。

答案 1 :(得分:7)

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

答案 2 :(得分:4)

因为“IEnumerable”说“来,列举我”(然后你说 - 如何,给我调查员),然而“IEnumerator”说“我可以列举你的收藏!”你已经拥有它,你不需要再获得它了。

答案 3 :(得分:2)

你在这里得到了很好的答案。只是一些重点。枚举器保持状态,它跟踪被枚举的集合中的当前对象。可通过IEnumerator.Current获得。它知道如何更改该状态,IEnumerator.MoveNext()。保持状态需要一个存储状态的单独对象。该状态无法轻松存储在集合对象中,因为只有一个集合对象,但可以有多个枚举器。

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

他们想出了一个非常好的技巧,WeakReference可能会受到这种启发。看看Reflector显示的代码。鼓舞人心的东西。挖掘更多,并在集合类中找到“版本”。太棒了。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

minlinq阅读Bart De Smet的帖子之后,我不再认为必须拆分这两个接口。

例如 - 在上面的链接中,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>());
}