我想将内部列表公开为迭代器,因此调用方法不会局限于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。
答案 0 :(得分:4)
问题是每次调用Enumerator
属性时都会创建新的Iterator
- 因此while
循环和Add
调用将新的迭代器重置为启动状态(和因此Current
是null
)。
正确的代码是
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;
}