C#“生成器”方法

Dan*_*Dan 3 c# foreach generator enumerator

我来自 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?
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道这并不完美,因为它是一个最小的工作示例(我不允许公开设置其中许多属性,但为了保持简单,我不想键入私有成员和属性)。

但是,我的主要问题是如何让使用 foreach 语句循环 MyClass 的调用者知道没有更多项目可供循环遍历?我是否抛出异常来指示没有剩余元素?

pok*_*oke 5

正如评论中提到的,您应该使用IEnumerable<T>而不是IEnumerator<T>. 枚举器是用于枚举某些事物的技术对象。在许多情况下,某些东西\xe2\x80\x94\xe2\x80\x93 是一个可枚举的。

\n\n

C# 具有处理枚举的特殊能力。最突出的是,您可以将foreach循环与可枚举值一起使用(但不能使用枚举器;即使循环实际上使用可枚举值的枚举器)。此外,可枚举允许您使用LINQ,这使其更易于使用。

\n\n

所以你应该像这样改变你的类:

\n\n
class MyClass\n{\n    public uint myLength { get; set; }\n    public uint myPageSize { get; set; }\n\n    # note the modified signature\n    public IEnumerable<Page> GetPages()\n    {\n        for (uint i = 1; i < this.myLength; i++)\n        {\n            Page p;\n            try\n            {\n                p = new Page(this, i);\n            }\n            catch (ArgumentOutOfRangeException)\n            {\n                yield break;\n            }\n            yield return p;\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后,这允许您像这样使用它:

\n\n
var obj = new MyClass();\n\nforeach (var page in obj.GetPages())\n{\n    // do whatever\n}\n\n// or even using LINQ\nvar pageOffsets = obj.GetPages().Select(p => p.currentOffset).ToList();\n
Run Code Online (Sandbox Code Playgroud)\n\n

当然,您还应该将方法的名称更改为有意义的名称。如果您\xe2\x80\x99返回页面,GetPages这可能是朝着正确方向迈出的良好第一步。该名称GetEnumerator是为实现 的类型保留的IEnumerable,其中该GetEnumerator方法应该返回对象表示的集合的枚举器。

\n