我真的需要实施 IEnumerable 吗?什么是 System.Collections.IEnumerator?
Do I REALLY need to implement IEnumerable? What is System.Collections.IEnumerator?
我有两个问题:
第一题:
我编写了一个扫描程序,它根据解析器的需要生成令牌流。
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 class 的唯一情况,您认为这样可以还是 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
我应该从对象做一个低效的向下转换到令牌)。
有人能解释一下吗?
IEnumerable<T>
扩展了 IEnumerable
,因此如果您正在实施 IEnumerable<T>
,您也会自动实施 IEnumerable
.
IEnumerable
的存在有一个原因:因为 C# 1.0 没有泛型。因此,我们现在必须一直实施 IEnumerable
,这有点烦人。
您的实现是正确的:实现 IEnumerable 时,只需调用通用版本即可。
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
IEnumerable<T>
与 IEnumerator<T>
不同的目的是表示您尚未开始枚举的一系列值。您可以像值的容器一样存储和传递它,当您想知道它包含什么时,您可以通过调用 GetEnumerator
.
开始枚举它
或者更常见的是,您会使用 foreach
循环或 LINQ 方法(例如 Select
和 Where
来循环遍历它。这些方法适用于 IEnumerable<T>
,不适用于 IEnumerator<T>
。
如果你只实现 IEnumerator<T>
那么枚举只能发生一次,在向前的方向。要重复枚举,用户必须再次调用您的方法以创建另一个枚举器。
所以 IEnumerable<T>
是 "factory pattern" 的一个实例。这是一家制造 IEnumerator<T>
.
的工厂
(关于非泛型 IEnumerable
的奇怪细节,正如@Dennis_E 所说,那只是古老的历史。您转发到泛型版本的实现是完全标准的。)
正如@DmitryBychenko 在评论中所说,IEnumerator<T>
基于 IDisposable
。 foreach
循环负责在其上调用 Dispose
。如果序列最终使用 yield return
实现,Dispose
的效果是调用任何尚未执行的 finally 块。
我有两个问题:
第一题:
我编写了一个扫描程序,它根据解析器的需要生成令牌流。
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 class 的唯一情况,您认为这样可以还是 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
我应该从对象做一个低效的向下转换到令牌)。
有人能解释一下吗?
IEnumerable<T>
扩展了 IEnumerable
,因此如果您正在实施 IEnumerable<T>
,您也会自动实施 IEnumerable
.
IEnumerable
的存在有一个原因:因为 C# 1.0 没有泛型。因此,我们现在必须一直实施 IEnumerable
,这有点烦人。
您的实现是正确的:实现 IEnumerable 时,只需调用通用版本即可。
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
IEnumerable<T>
与 IEnumerator<T>
不同的目的是表示您尚未开始枚举的一系列值。您可以像值的容器一样存储和传递它,当您想知道它包含什么时,您可以通过调用 GetEnumerator
.
或者更常见的是,您会使用 foreach
循环或 LINQ 方法(例如 Select
和 Where
来循环遍历它。这些方法适用于 IEnumerable<T>
,不适用于 IEnumerator<T>
。
如果你只实现 IEnumerator<T>
那么枚举只能发生一次,在向前的方向。要重复枚举,用户必须再次调用您的方法以创建另一个枚举器。
所以 IEnumerable<T>
是 "factory pattern" 的一个实例。这是一家制造 IEnumerator<T>
.
(关于非泛型 IEnumerable
的奇怪细节,正如@Dennis_E 所说,那只是古老的历史。您转发到泛型版本的实现是完全标准的。)
正如@DmitryBychenko 在评论中所说,IEnumerator<T>
基于 IDisposable
。 foreach
循环负责在其上调用 Dispose
。如果序列最终使用 yield return
实现,Dispose
的效果是调用任何尚未执行的 finally 块。