如何处理"无限"IEnumerable?

Dan*_*vil 26 c# enumeration infinite-loop yield-return

"无限"IEnumerable的一个简单例子

IEnumerable<int> Numbers() {
  int i=0;
  while(true) {
    yield return unchecked(i++);
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道

foreach(int i in Numbers().Take(10)) {
  Console.WriteLine(i);
}
Run Code Online (Sandbox Code Playgroud)

var q = Numbers();
foreach(int i in q.Take(10)) {
  Console.WriteLine(i);
}
Run Code Online (Sandbox Code Playgroud)

两者都工作正常(并打印数字0-9).

但复制或处理表达式时是否有任何陷阱q?我可以依赖这样一个事实,即它们总是被评估为"懒惰"吗?产生无限循环有危险吗?

Mar*_*ell 19

只要你只调用懒惰的,无缓冲的方法,你应该没问题.所以Skip,Take,Select,等都是精品.然而Min,Count,OrderBy等会发疯的.

它可以工作,但你需要谨慎.或者注入一个Take(somethingFinite)安全措施(或一些其他自定义扩展方法,在太多数据后抛出异常).

例如:

public static IEnumerable<T> SanityCheck<T>(this IEnumerable<T> data, int max) {
    int i = 0;
    foreach(T item in data) {
        if(++i >= max) throw new InvalidOperationException();
        yield return item;
    }
}
Run Code Online (Sandbox Code Playgroud)


Ada*_*son 8

是的,您可以保证上面的代码会被懒散地执行.虽然它看起来(在你的代码中)就像你永远循环一样,但你的代码实际上产生了这样的东西:

IEnumerable<int> Numbers()
{
    return new PrivateNumbersEnumerable();
}

private class PrivateNumbersEnumerable : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator() 
    { 
        return new PrivateNumbersEnumerator(); 
    }
}

private class PrivateNumbersEnumerator : IEnumerator<int>
{
    private int i;

    public bool MoveNext() { i++; return true; }   

    public int Current
    {
        get { return i; }
    }
}
Run Code Online (Sandbox Code Playgroud)

(这显然是不准确究竟会产生,因为这是非常具体的,以你的代码,但它仍然相似,应该告诉你为什么它会被懒惰地评估).


spo*_*son 5

你必须避免任何试图读取的贪婪函数.这将包括Enumerable像扩展:Count,ToArray/ ToList,和骨料Avg/ Min/ Max

无限懒惰列表没有任何问题,但你必须有意识地决定如何处理它们.

用于Take通过设置上限来限制无限循环的影响,即使您不需要它们也是如此.