【问题标题】:Do I REALLY need to implement IEnumerable? What is System.Collections.IEnumerator?我真的需要实现 IEnumerable 吗?什么是 System.Collections.IEnumerator?
【发布时间】:2015-02-05 10:49:20
【问题描述】:

我有两个问题:

第一个问题:

我编写了一个 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&lt;T&gt; 而不是IEnumerable(否则 GetEnumerator() 就足够了,但是在Current 上我应该从对象到令牌进行低效的向下转换)。

谁能解释一下?

【问题讨论】:

    标签: c# generics iterator ienumerable


    【解决方案1】:

    IEnumerable&lt;T&gt; 扩展了IEnumerable,所以如果你正在实现IEnumerable&lt;T&gt;,你也会自动实现IEnumerable

    IEnumerable 存在的原因有一个:因为 C# 1.0 没有泛型。因此,我们现在必须一直实现IEnumerable,这有点烦人。

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

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

    【讨论】:

    • 感谢您对第二个问题的回答!我有两个问题要问你: 1. 第一个问题呢? 2.有什么特别的原因,因为我应该以不同的方式实现IEnumerable.GetEnumerator()
    • IEnumerable 不仅仅是一个遗产。它可以用来枚举对象。不是泛型的,而是泛化的枚举方式。
    • @TIKSN 这几乎是遗产。 IEnumerable&lt;object&gt; 更可取,因为它适合 LINQ。
    • @justHelloWorld 实现 IEnumerable 的一个原因是您可以在 foreach 循环中使用它并使用 LINQ 等等。
    • @Dennis_E 无论如何你都可以使用 foreach 循环(我刚刚尝试过)!我不知道 LINQ 怎么样
    【解决方案2】:

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

    或者更常见的是,您会使用foreach 循环或像SelectWhere 这样的LINQ 方法来循环它。这些方法适用于IEnumerable&lt;T&gt;,不适用于IEnumerator&lt;T&gt;

    如果你只实现IEnumerator&lt;T&gt;,那么枚举只能在向前的方向上发生一次。要重复枚举,用户必须再次调用您的方法来创建另一个枚举器。

    所以IEnumerable&lt;T&gt; 是“工厂模式”的一个实例。这是一个制造IEnumerator&lt;T&gt;的工厂。

    (至于关于非泛型 IEnumerable 的奇怪细节,正如 @Dennis_E 所说,那只是古老的历史。您转发到泛型版本的实现是完全标准的。)

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

    【讨论】:

    • 还有一个原因是IEnumerator&lt;T&gt;IDisposable,所以当IEnumerable&lt;T&gt; 隐藏资源分配/释放时,我们必须关闭分配的资源(通过using
    • @DmitryBychenko - 优秀 点 - 将更新以提及 foreach 调用 Dispose
    猜你喜欢
    • 1970-01-01
    • 2014-08-15
    • 1970-01-01
    • 2019-08-28
    • 1970-01-01
    • 2012-12-03
    • 2011-05-10
    • 2014-10-07
    • 2011-05-18
    相关资源
    最近更新 更多