我真的需要实现IEnumerable吗?什么是System.Collections.IEnumerator?

时间:2015-02-05 10:49:20

标签: c# generics iterator ienumerable

我有两个问题:

第一个问题:

我写了一个Scanner,它根据Parser的需要生成一个令牌流。

public class Scanner
{
    public IEnumerator<Token> GetEnumerator()
    { //DO SOME STUFF HERE
      yield return new Token(...);
    }
}

public class Parser
{
    IEnumerator<Token> iterator;
    ...
    public Parser(Scanner scanner)
    {
        this.iterator = scanner.GetEnumerator(); 
        ...
    }
    public Token nextToken()
    {
        //generate the next token
        iterator.MoveNext();//there is no need to test MoveNext()
        return iterator.Current;//get the generated token
    }
    ...
}

考虑到这实际上是我在整个框架中使用Scanner类的唯一情况,您认为没有问题或Scanner实现IEnumerable会更正确吗?

如果我没错,那么编译器会做一些操作让Scanner实现IEnumerable,这是正确的吗?

第二个问题:

假设Scanner实现了IEnumerable,那么代码将如下所示:

public class Scanner:IEnumerable<Token>
{
public IEnumerator<Token> GetEnumerator()
{
    ...
}

IEnumerator IEnumerable.GetEnumerator()
{
   return this.GetEnumerator();//I'M NOT SURE OF THIS!
}

我不明白的是IEnumerable.GetEnumerator()的含义是什么,以及我的实施是否正确。我知道有必要,因为Scanner实现IEnumerable<T>而不是IEnumerable(否则GetEnumerator()就足够了,但是然后在Current我应该从对象做一个低效的向下转换to Token)。

有人可以进行宣传吗?

2 个答案:

答案 0 :(得分:3)

IEnumerable<T>扩展了IEnumerable,因此,如果您要实施IEnumerable<T>,则会自动实施IEnumerable

IEnumerable存在的原因之一是:因为C#1.0没有泛型。因此,我们现在必须始终实施IEnumerable,这有点令人讨厌。

您的实现是正确的:在实现IEnumerable时,只需调用通用版本。

IEnumerator IEnumerable.GetEnumerator()
{
   return this.GetEnumerator();
}

答案 1 :(得分:2)

IEnumerable<T>不同的IEnumerator<T>的目的是表示您尚未开始枚举的一系列值。您可以像容器值一样存储和传递它,当您想知道它包含什么时,您可以通过调用GetEnumerator来开始枚举它。

或者更常见的是,您使用foreach循环或LINQ方法(如SelectWhere来循环播放它。这些方法适用于IEnumerable<T>,而不适用于IEnumerator<T>

如果您只实施IEnumerator<T>,那么枚举只能在前进方向上进行一次。要重复枚举,用户必须再次调用您的方法来创建另一个枚举器。

所以IEnumerable<T>是&#34;工厂模式&#34;的一个实例。它是一家制造IEnumerator<T>的工厂。

(至于关于非泛型IEnumerable的奇怪细节,这就像@Dennis_E所说的那样古老的历史。你转发到通用版本的实现是完全标准的。)

正如@DmitryBychenko在评论中所说,IEnumerator<T>基于IDisposableforeach循环负责在其上调用Dispose。如果序列最终使用yield return实现,Dispose具有调用尚未执行的任何finally块的效果。