How does IEnumerable work in background

前端 未结 3 1565
天命终不由人
天命终不由人 2021-01-15 08:54

I am wandering about the more in-depth functionality of the IEnumerable interface.

Basically, it works as an intermediary step in execution. Fo

3条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-15 09:30

    Not all objects that implement IEnumerable defer execution in some way. The API of the interface makes it possible to defer execution, but it doesn't require it. There are likewise implementations that don't defer execution in any way.

    So, what is the collection the actual items (in the example 2*x items) reside in?

    There is none. Whenever the next value is requested it computes that one value on demand, gives it to the caller, and then forgets the value. It doesn't store it anywhere else.

    Moreover, if we were to write IEnumerable temp = Enumerable.Repeat(1, 10);, what would be the underlying collection where the 1s are stored (array, list, something else)?

    There wouldn't be one. It would compute each new value immediately when you ask for the next value and it won't remember it afterward. It only stores enough information to be able to compute the next value, which means it only needs to store the element and the number of values left to yield.

    While the actual .NET implementations will use much more concise means of creating such a type, creating an enumerable that defers execution is not particularly hard. Doing so even the long way is more tedious than difficult. You simply compute the next value in the MoveNext method of the iterator. In the example you asked of, Repeat, this is easy as you only need to compute if there is another value, not what it is:

    public class Repeater : IEnumerator
    {
        private int count;
        private T element;
    
        public Repeater(T element, int count)
        {
            this.element = element;
            this.count = count;
        }
        public T Current { get { return element; } }
    
        object IEnumerator.Current
        {
            get { return Current; }
        }
    
        public void Dispose() { }
    
        public bool MoveNext()
        {
            if (count > 0)
            {
                count--;
                return true;
            }
            else
                return false;
        }
    
        public void Reset()
        {
            throw new NotSupportedException();
        }
    }
    

    (I've omitted an IEnumerable type that just returns a new instance of this type, or a static Repeat method that creates a new instance of that enumerable. There isn't anything particularly interesting to see there.)

    A slightly more interesting example would be something like Count:

    public class Counter : IEnumerator
    {
        private int remaining;
    
        public Counter(int start, int count)
        {
            Current = start;
            this.remaining = count;
        }
        public int Current { get; private set; }
    
        object IEnumerator.Current
        {
            get { return Current; }
        }
    
        public void Dispose() { }
    
        public bool MoveNext()
        {
            if (remaining > 0)
            {
                remaining--;
                Current++;
                return true;
            }
            else
                return false;
        }
    
        public void Reset()
        {
            throw new NotSupportedException();
        }
    }
    

    Here we're not only computing if we have another value, but what that next value is, each time a new value is requested of us.

提交回复
热议问题