IEnumerator <string> .Current返回null </string>

时间:2014-01-30 06:08:26

标签: c# yield-return ienumerator

我想将内部列表公开为迭代器,因此调用方法不会局限于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;
    }
}

并且两者都导致test在以下测试中出现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始终为null。

2 个答案:

答案 0 :(得分:4)

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

正确的代码是

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

我认为这部分是由于惯例导致财产无论多久被调用都会返回“便宜且相同”的价值。另一方面,“GetXXXXX”方法预计会返回一些可能在调用之间发生变化的方法。您可以在IEnumerable<T>上看到该模式 - 它通过方法(GetEnumerator)为您提供迭代器,鼓励存储值并使用它。

答案 1 :(得分:0)

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

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;
}
相关问题