Get next N elements from enumerable

后端 未结 10 1495
遥遥无期
遥遥无期 2020-12-16 17:38

Context: C# 3.0, .Net 3.5
Suppose I have a method that generates random numbers (forever):

private static IEnumerable RandomNumberGenerator(         


        
10条回答
  •  南方客
    南方客 (楼主)
    2020-12-16 18:03

    It seems like we'd prefer for an IEnumerable to have a fixed position counter so that we can do

    var group1 = items.Take(10);
    var group2 = items.Take(10);
    var group3 = items.Take(10);
    var group4 = items.Take(10);
    

    and get successive slices rather than getting the first 10 items each time. We can do that with a new implementation of IEnumerable which keeps one instance of its Enumerator and returns it on every call of GetEnumerator:

    public class StickyEnumerable : IEnumerable, IDisposable
    {
        private IEnumerator innerEnumerator;
    
        public StickyEnumerable( IEnumerable items )
        {
            innerEnumerator = items.GetEnumerator();
        }
    
        public IEnumerator GetEnumerator()
        {
            return innerEnumerator;
        }
    
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return innerEnumerator;
        }
    
        public void Dispose()
        {
            if (innerEnumerator != null)
            {
                innerEnumerator.Dispose();
            }
        }
    }
    

    Given that class, we could implement Slice with

    public static IEnumerable> Slices(this IEnumerable items, int size)
    {
        using (StickyEnumerable sticky = new StickyEnumerable(items))
        {
            IEnumerable slice;
            do
            {
                slice = sticky.Take(size).ToList();
                yield return slice;
            } while (slice.Count() == size);
        }
        yield break;
    }
    

    That works in this case, but StickyEnumerable is generally a dangerous class to have around if the consuming code isn't expecting it. For example,

    using (var sticky = new StickyEnumerable(Enumerable.Range(1, 10)))
    {
        var first = sticky.Take(2);
        var second = sticky.Take(2);
        foreach (int i in second)
        {
            Console.WriteLine(i);
        }
        foreach (int i in first)
        {
            Console.WriteLine(i);
        }
    }
    

    prints

    1
    2
    3
    4
    

    rather than

    3
    4
    1
    2
    

提交回复
热议问题