C# 如何设计(理论上)返回无限数量项的枚举器?

C# 如何设计(理论上)返回无限数量项的枚举器?,c#,enumerator,C#,Enumerator,我正在编写类似以下内容的代码: public IEnumerable<T> Unfold<T>(this T seed) { while (true) { yield return [next (T)object in custom sequence]; } } public IEnumerable展开(此T种子) { while(true) { 收益返回[自定义序列中的下一个(T)对象]; } } 显然,这种方法永远不会返回。

我正在编写类似以下内容的代码:

public IEnumerable<T> Unfold<T>(this T seed)
{
    while (true)
    {
        yield return [next (T)object in custom sequence];
    }
}
public IEnumerable展开(此T种子)
{
while(true)
{
收益返回[自定义序列中的下一个(T)对象];
}
}
显然,这种方法永远不会返回。(C#编译器默默地允许这样做,而R#则警告我“函数永远不会返回”。)

一般来说,提供一个返回无限多个项的枚举器,而不提供停止枚举的方法,这是一个糟糕的设计吗

在这种情况下有什么特别的考虑吗?记忆?性能?其他哥特查

如果我们总是提供退出条件,有哪些选项?例如:

  • 表示包含或排除边界的T类型的对象
  • 一个
    谓词continue
    (正如
    TakeWhile
    所做的那样)
  • 计数(如
    所做的那样)
我们是否应该依靠用户在
展开(…)
之后调用
Take(…)
?(可能是首选选项,因为它利用了现有的Linq知识。)


如果代码将在公共API中发布,无论是作为(通用)还是作为此模式的特定实现,您是否会以不同的方式回答这个问题?

只要您非常清楚地记录该方法将永远不会完成迭代(当然,该方法本身返回得非常快),那么我认为这是可以的。事实上,它可以使一些算法更加整洁。我不相信有任何显著的内存/性能影响-尽管如果您在迭代器中引用“昂贵”的对象,该引用将被捕获

滥用API的方式总是有的:只要你的文档清晰,我认为这没关系

“总的来说,设计不好吗 提供一个返回 无限数量的物品,没有 提供停止枚举的方法?”

代码的使用者可以始终停止枚举(例如使用break或其他方法)。如果您的枚举数返回和无限序列,这并不意味着枚举数的客户机以某种方式被强制从不中断枚举,实际上您不能创建一个保证由客户机完全枚举的枚举数

我们应该依靠用户的电话吗 在…之后取(…)/TakeWhile(…) 展开(…)?(可能是首选 选项,因为它利用了现有的 Linq知识。)

是的,只要您在文档中明确指定枚举器返回和无限序列以及枚举的中断是调用方的责任,一切都应该是好的


返回无限序列不是一个坏主意,函数编程语言已经做了很长时间了

我同意乔恩的看法。编译器将您的方法转换为实现简单状态机的类,该类保持对当前值(即将通过当前属性返回的值)的引用。我多次使用这种方法来简化代码。如果您清楚地记录了该方法的行为,它应该可以正常工作。

我不会在公共API中使用无限枚举器。C#程序员,包括我自己,都太习惯foreach循环了。这也将与.NET框架保持一致;注意Enumerable.Range和Enumerable.Repeat方法如何使用参数来限制可枚举项的数量。Microsoft选择使用Enumerable.Repeat(“,10”)而不是Enumerable.Repeat(”)。选择(10)以避免无限枚举,我将坚持他们的设计选择。

C#开发人员应该开始学习枚举器枚举序列(有限或无限)而不是列表。有限生成器并不适用于所有类型的问题,有时你无法知道事先需要多少个元素。重复/范围的目的是在给定的迭代次数内重复-这就是为什么有这个数目。在其他场景中,拥有无限序列是完全合理的。除了文档之外,我认为如果可能的话,方法名应该表明枚举是无限的。例如,“IEnumerable GetAllPrimeNumbers()”很好,但我不确定是否有像Unfold这样的名称。“Unfold”是高阶函数的名称,它与“fold”相反,在一般上下文中是一个有效的名称。至于具体的实现,是的,更好的命名是可取的。顺便说一句,聚合方法是“fold”函数的Linq实现,据我所知,没有内置的反向实现。如果我错了,请纠正我。SelectMany可能是最接近展开的,但它不是直接映射。它可以让你轻松地展开一系列序列,而不是“某些序列和某些元素”的序列。顺便说一句,你会发现关于你的问题,这很有趣: