我有两个问题:
第一个问题:
我写了一个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)。
有人可以进行宣传吗?
答案 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方法(如Select
和Where
来循环播放它。这些方法适用于IEnumerable<T>
,而不适用于IEnumerator<T>
。
如果您只实施IEnumerator<T>
,那么枚举只能在前进方向上进行一次。要重复枚举,用户必须再次调用您的方法来创建另一个枚举器。
所以IEnumerable<T>
是&#34;工厂模式&#34;的一个实例。它是一家制造IEnumerator<T>
的工厂。
(至于关于非泛型IEnumerable
的奇怪细节,这就像@Dennis_E所说的那样古老的历史。你转发到通用版本的实现是完全标准的。)
正如@DmitryBychenko在评论中所说,IEnumerator<T>
基于IDisposable
。 foreach
循环负责在其上调用Dispose
。如果序列最终使用yield return
实现,Dispose
具有调用尚未执行的任何finally块的效果。