C#“生成器"方法 [英] C# "Generator" Method
问题描述
我来自Python世界,正在尝试用C#创建一个生成器"方法.我正在按特定缓冲区大小的块来解析文件,并且只想一次读取和存储下一个块,并在foreach
循环中产生它.这是我到目前为止(简化的概念证明):
I come from the world of Python and am trying to create a "generator" method in C#. I'm parsing a file in chunks of a specific buffer size, and only want to read and store the next chunk one at a time and yield it in a foreach
loop. Here's what I have so far (simplified proof of concept):
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?
}
}
}
}
我知道这不是完美的,因为这是一个最低限度的工作示例(我不允许公开设置许多这些属性,但是为了保持简单性,我不想键入私有成员和属性).
I know this is not perfect since it is a minimum working example (I don't allow many of these properties to be set publicly, but for keeping this simple I don't want to type private members and properties).
但是,我的主要问题是如何让调用者使用foreach语句在MyClass上循环,从而知道没有其他项目可以循环了?我是否抛出异常以指示还没有元素?
However, my main question is how do I let the caller looping over MyClass with a foreach statement know that there are no more items left to loop through? Is there an exception I throw to indicate there are no elements left?
推荐答案
如注释中所述,您应该使用IEnumerable<T>
而不是IEnumerator<T>
.枚举器是用于枚举某些内容的技术对象.在许多情况下,这些东西是可以枚举的.
As mentioned in the comments, you should use IEnumerable<T>
instead of IEnumerator<T>
. The enumerator is the technical object that is being used to enumerate over something. That something—in many cases–is an enumerable.
C#具有处理枚举的特殊功能.最显着的是,您可以将foreach
循环用于可枚举(但不能使用枚举器;即使该循环实际上使用了可枚举的枚举器).另外,可枚举允许您使用 LINQ ,这使得它更容易实现消费.
C# has special abilities to deal with enumerables. Most prominently, you can use a foreach
loop with an enumerable (but not an enumerator; even though the loop actually uses the enumerator of the enumerable). Also, enumerables allow you to use LINQ which makes it even more easier to consume.
所以您应该像这样更改班级:
So you should change your class like this:
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;
}
}
}
最后,您可以像这样使用它:
In the end, this allows you to use it like this:
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();
当然,您还应该将方法的 name 更改为有意义的名称.如果您要返回页面,则GetPages
可能是朝着正确方向迈出的良好第一步.名称GetEnumerator
是为实现IEnumerable
的类型保留的,其中GetEnumerator
方法应该返回对象表示的集合的枚举数.
Of course, you should also change the name of the method to something meaningful. If you’re returning pages, GetPages
is maybe a good first step in the right direction. The name GetEnumerator
is kind of reserved for types implementing IEnumerable
, where the GetEnumerator
method is supposed to return an enumerator of the collection the object represents.
这篇关于C#“生成器"方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!