C# "Generator"方法

标签 c# foreach generator enumerator

我来自 Python 世界,正在尝试在 C# 中创建一个“生成器”方法。我正在以特定缓冲区大小的 block 解析文件,并且只想一次读取和存储下一个 block 并在 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?
            }
        }
    }
}

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

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

最佳答案

如评论中所述,您应该使用 IEnumerable<T>而不是 IEnumerator<T> .枚举器是用于枚举某物的技术对象。在许多情况下,某些东西是可枚举的。

C# 具有处理枚举的特殊能力。最突出的是,您可以使用 foreach使用可枚举的循环(但不是枚举器;即使循环实际上使用可枚举的枚举器)。此外,枚举允许您使用 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方法应该返回对象表示的集合的枚举数。

关于C# "Generator"方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38274727/

相关文章:

mysql - 查询计数并根据字段重置

java - 如何洗牌对

c# - Linq to Xml : selecting elements if an attribute value equals a node value in an IEnumerable<XElement>

c# - RestSharp 方法抛出 System.Xml.XMlException "="是意外标记。预期的 token 是 ';'

azure - 使用 for_each 循环创建的多个子网的值的输出

java - for each 循环中 if 语句的返回值

foreach - smarty中如何检查迭代?

python-3.x - 如何确定for循环是异常退出还是从迭代器内部中断

c#从excel文件中获取值列表

c# - <T> 或 Object 上的扩展方法之间有什么实际区别吗?