【问题标题】:IEnumerator<string>.Current returns always nullIEnumerator<string>.Current 始终返回 null
【发布时间】:2014-02-22 07:38:56
【问题描述】:

我想将内部列表公开为迭代器,因此调用方法将不限于 foreach 循环,但它会随时调用 IEnumerator.Current 和 IEnumerator.MoveNext()。 我尝试了两种方法:

public IEnumerator<string> Iterator
{
    get
    {
        return m_list.GetEnumerator();
    }
}

public IEnumerator<string> Iterator
{
    get
    {
        for (int i = 0; i < m_list.Count; i++)
        {
            yield return m_list[i];
        }
        yield break;
    }
}

并且两者都导致测试在以下测试中出现 OutOfMemoryException:

[TestMethod]
public void TestMethod1()
{
   var countryCode = "US";
   var countryProvider= new CountryProvider(countryCode);
   var filteredList = new List<string>();

   while(countryProvider.Iterator.MoveNext())
   {
       filteredList.Add(countryProvider.Iterator.Current);
   }

   Assert.IsTrue(filteredEFIs.Count > 0);        
}

当我尝试调试器时,我注意到每次调用 MoveNext() 时它都会从头开始计数并且 Iterator.Current 始终为空。

【问题讨论】:

  • 你能展示完整的 CountryProvider 类吗?
  • 是的...不知道你的问题是什么 - 在 while 的每次迭代中创建新迭代器的调用方法应该完全按照你的描述行事。
  • 为什么不返回IEnumerable&lt;T&gt; 而不是枚举数?实现是一样的,调用者会更简单,而且 - 它会工作!
  • 您也可以直接返回列表。如果您担心调用者将 ienumerable 向下转换到列表(有些人是,我不是),您可以创建一个 ReadonlyCollection,执行 .ToArray 或 .Select(x => x)。

标签: c# yield-return ienumerator


【解决方案1】:

问题是每次调用Iterator 属性时都会创建新的Enumerator - 因此while 循环和Add 调用都将新迭代器重置为起始状态(因此Currentnull )。

正确的代码是

var iterator = countryProvider.Iterator;
while(iterator.MoveNext())
   {
       filteredList.Add(iterator.Current);
   }

我相信这部分是由约定造成的,即无论多久调用一次,属性都会返回“便宜且相同”的值。另一方面,“GetXXXXX”方法预计会返回一些可能在调用之间发生变化的东西。您可以在 IEnumerable&lt;T&gt; 上看到该模式 - 它通过方法 (GetEnumerator) 为您提供迭代器,鼓励存储值并使用它。

【讨论】:

    【解决方案2】:

    感谢您解释实例问题。我提供了一个解决方案,它不会破坏调用者并控制提供者类中的迭代器实例。场景我假设调用者知道两件事 - 当它开始会话时,当它想要下一个项目时,从这里开始的方式很少,其他将迭代器保留在调用者内部(稍微破坏实现)或在本地缓存它,除非故意重置不会破坏当前调用者实现(如下所示)。另一种方法是使用装饰器模式包装内部枚举器(如下)...

    private IEnumerator<string> m_iterator;
    
    public string Iterator
    {
        get
        {
            if (this.m_iterator == null)
            {
                 this.ResetIterator();
            }
    
            return this.m_iterator.MoveNext();
        }
    }
    
    public void ResetIterator()
    {
        this.m_iterator = this.m_internalIterator();
    }
    
    private IEnumerator<string> m_internalIterator()
    {
        for (int i = 0; i < m_list.Count; i++)
        {
            yield return m_list[i];
        }
        yield break;
    }
    

    通过装饰器模式,假设我的 Provider 类型实现了 IEnumerator:

    private IEnumerator<string> m_iterator;
    
    public bool MoveNext()
    {
        if (this.m_iterator == null)
        {
            this.ResetIterator();
        }
        return this.m_iterator.MoveNext();        
    }
    
    public string Current
    {
        get
        {
            if (this.m_iterator == null)
            {
                this.Reset();
            }
            return this.m_iterator.Current;
        }
    }
    
    public void Reset()
    {
        this.m_iterator = this.m_internalIterator();
    }
    
    private IEnumerator<string> m_internalIterator()
    {
        for (int i = 0; i < m_list.Count; i++)
        {
            yield return m_list[i];
        }
        yield break;
    }
    

    【讨论】:

      猜你喜欢
      • 2019-01-12
      • 2016-05-31
      • 2015-08-15
      • 2012-03-18
      • 2016-11-04
      • 2016-07-28
      • 2010-11-08
      • 2017-11-26
      • 2015-02-23
      相关资源
      最近更新 更多