我来自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,知道没有更多的项目需要循环?我抛出是否有异常表明没有剩余元素?
答案 0 :(得分:1)
执行此操作的两种方法是让代码执行到达GetEnumerator函数的末尾或在代码中放入yield break;
,这与函数中的return;
的行为相同已退回void
。
从来电者的观察中,GetEnumerator()
返回的枚举器将开始返回false
MoveNext()
,这就是他们告诉枚举器完成的方式。
要修复“无法使用catch子句在try块的主体内生成值”,将try / catch放在代码的错误部分周围,execption将被抛出{{1}不是new
。您的代码应该是
yield return
答案 1 :(得分:1)
如评论中所述,您应使用IEnumerable<T>
代替IEnumerator<T>
。枚举器是用于枚举某些东西的技术对象。在许多情况下,这个东西是可以枚举的。
C#具有处理枚举的特殊能力。最突出的是,你可以使用带有可枚举的foreach
循环(但不是枚举器;即使循环实际上使用了枚举的枚举器)。此外,enumerables允许您使用LINQ,这使得它更容易消费。
所以你应该像这样改变你的课程:
class MyClass
{
public uint myLength { get; set; }
public uint myPageSize { get; set; }
# note the modified signature
public IEnumerable<Page> GetPages()
{
for (uint i = 1; i < this.myLength; i++)
{
Page p;
try
{
p = new Page(this, i);
}
catch (ArgumentOutOfRangeException)
{
yield break;
}
yield return p;
}
}
}
最后,这允许你像这样使用它:
var obj = new MyClass();
foreach (var page in obj.GetPages())
{
// do whatever
}
// or even using LINQ
var pageOffsets = obj.GetPages().Select(p => p.currentOffset).ToList();
当然,您还应该将方法的名称更改为有意义的内容。如果您要返回页面,GetPages
可能是朝着正确方向迈出的良好开端。名称GetEnumerator
是为实现IEnumerable
的类型保留的,其中GetEnumerator
方法应返回对象所代表的集合的枚举器。
答案 2 :(得分:0)
使用yield break;
语句结束迭代器方法生成的序列。