【发布时间】:2016-11-11 11:58:36
【问题描述】:
我来自 Python 世界,正在尝试在 C# 中创建一个“生成器”方法。我正在以特定缓冲区大小的块解析文件,并且只想一次读取和存储下一个块并在foreach 循环中生成它。到目前为止,这是我所拥有的(简化的概念证明):
class Page
{
public uint StartOffset { get; set; }
private uint currentOffset = 0;
public Page(MyClass c, uint pageNumber)
{
uint StartOffset = pageNumber * c.myPageSize;
if (StartOffset < c.myLength)
currentOffset = StartOffset;
else
throw new ArgumentOutOfRangeException("Page offset exceeds end of file");
while (currentOffset < c.myLength && currentOffset < (StartOffset + c.myPageSize))
// read data from page and populate members (not shown for MWE purposes)
. . .
}
}
class MyClass
{
public uint myLength { get; set; }
public uint myPageSize { get; set; }
public IEnumerator<Page> GetEnumerator()
{
for (uint i = 1; i < this.myLength; i++)
{
// start count at 1 to skip first page
Page p = new Page(this, i);
try
{
yield return p;
}
catch (ArgumentOutOfRangeException)
{
// end of available pages, how to signal calling foreach loop?
}
}
}
}
我知道这并不完美,因为它是一个最低限度的工作示例(我不允许公开设置其中的许多属性,但为了保持简单,我不想键入私有成员和属性)。
但是,我的主要问题是如何让使用 foreach 语句遍历 MyClass 的调用者知道没有更多的项目可以循环了?有没有我抛出的异常表明没有剩余元素?
【问题讨论】:
-
您只需停止生成项目,就像在 Python 中一样。话虽如此,您应该创建一个返回
IEnumerable<BTreePage>的方法;枚举更容易使用。 -
IEnumerator<T>.MoveNext告诉调用者停止迭代。这是在您使用yield return时为您实现的。如果您想明确停止,可以使用yield break。 -
@poke 在示例中不一致是我的错。 Page 是这篇文章的虚构内容,BTreePage 确实是我在真实代码中返回的内容。固定。
-
我在评论使用
IEnumerable<T>与IEnumerator<T>。 -
@Dan 请参阅this question。基本上(因为您来自 Python),
IEnumerable是生成器或列表,而IEnumerator将是您在其上调用iter()时得到的东西(技术上的东西)。大多数时候,您会想要IEnumerable,因为它更容易使用(例如,使用foreach循环)。
标签: c# foreach generator enumerator