How to handle an “infinite” IEnumerable?

前端 未结 5 1699
醉梦人生
醉梦人生 2021-02-01 17:11

A trivial example of an \"infinite\" IEnumerable would be

IEnumerable Numbers() {
  int i=0;
  while(true) {
    yield return unchecked(i++);
  }
}


        
相关标签:
5条回答
  • 2021-02-01 17:43

    If it wasn't lazy evaluation, your first example won't work as expected in the first place.

    0 讨论(0)
  • 2021-02-01 17:52

    Yes, you are guaranteed that the code above will be executed lazily. While it looks (in your code) like you'd loop forever, your code actually produces something like this:

    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; }
        }
    }
    

    (This obviously isn't exactly what will be generated, since this is pretty specific to your code, but it's nonetheless similar and should show you why it's going to be lazily evaluated).

    0 讨论(0)
  • 2021-02-01 17:54

    You would have to avoid any greedy functions that attempt to read to end. This would include Enumerable extensions like: Count, ToArray/ToList, and aggregates Avg/Min/Max, etc.

    There's nothing wrong with infinite lazy lists, but you must make conscious decisions about how to handle them.

    Use Take to limit the impact of an endless loop by setting an upper bound even if you don't need them all.

    0 讨论(0)
  • 2021-02-01 17:59

    As long as you only call lazy, un-buffered methods you should be fine. So Skip, Take, Select, etc are fine. However, Min, Count, OrderBy etc would go crazy.

    It can work, but you need to be cautious. Or inject a Take(somethingFinite) as a safety measure (or some other custom extension method that throws an exception after too much data).

    For example:

    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;
        }
    }
    
    0 讨论(0)
  • 2021-02-01 18:02

    Yes, your code will always work without infinite looping. Someone might come along though later and mess things up. Suppose they want to do:

    var q = Numbers().ToList();
    

    Then, you're hosed! Many "aggregate" functions will kill you, like Max().

    0 讨论(0)
提交回复
热议问题