Is there a built-in way to convert IEnumerator to IEnumerable

前端 未结 8 1728
面向向阳花
面向向阳花 2020-12-30 18:55

Is there a built-in way to convert IEnumerator to IEnumerable?

相关标签:
8条回答
  • 2020-12-30 19:15

    This is a variant I have written... The specific is a little different. I wanted to do a MoveNext() on an IEnumerable<T>, check the result, and then roll everything in a new IEnumerator<T> that was "complete" (so that included even the element of the IEnumerable<T> I had already extracted)

    // Simple IEnumerable<T> that "uses" an IEnumerator<T> that has
    // already received a MoveNext(). "eats" the first MoveNext() 
    // received, then continues normally. For shortness, both IEnumerable<T>
    // and IEnumerator<T> are implemented by the same class. Note that if a
    // second call to GetEnumerator() is done, the "real" IEnumerator<T> will
    // be returned, not this proxy implementation.
    public class EnumerableFromStartedEnumerator<T> : IEnumerable<T>, IEnumerator<T>
    {
        public readonly IEnumerator<T> Enumerator;
    
        public readonly IEnumerable<T> Enumerable;
    
        // Received by creator. Return value of MoveNext() done by caller
        protected bool FirstMoveNextSuccessful { get; set; }
    
        // The Enumerator can be "used" only once, then a new enumerator
        // can be requested by Enumerable.GetEnumerator() 
        // (default = false)
        protected bool Used { get; set; }
    
        // The first MoveNext() has been already done (default = false)
        protected bool DoneMoveNext { get; set; }
    
        public EnumerableFromStartedEnumerator(IEnumerator<T> enumerator, bool firstMoveNextSuccessful, IEnumerable<T> enumerable)
        {
            Enumerator = enumerator;
            FirstMoveNextSuccessful = firstMoveNextSuccessful;
            Enumerable = enumerable;
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            if (Used)
            {
                return Enumerable.GetEnumerator();
            }
    
            Used = true;
            return this;
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        public T Current
        {
            get
            {
                // There are various school of though on what should
                // happens if called before the first MoveNext() or
                // after a MoveNext() returns false. We follow the 
                // "return default(TInner)" school of thought for the
                // before first MoveNext() and the "whatever the 
                // Enumerator wants" for the after a MoveNext() returns
                // false
                if (!DoneMoveNext)
                {
                    return default(T);
                }
    
                return Enumerator.Current;
            }
        }
    
        public void Dispose()
        {
            Enumerator.Dispose();
        }
    
        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }
    
        public bool MoveNext()
        {
            if (!DoneMoveNext)
            {
                DoneMoveNext = true;
                return FirstMoveNextSuccessful;
            }
    
            return Enumerator.MoveNext();
        }
    
        public void Reset()
        {
            // This will 99% throw :-) Not our problem.
            Enumerator.Reset();
    
            // So it is improbable we will arrive here
            DoneMoveNext = true;
        }
    }
    

    Use:

    var enumerable = someCollection<T>;
    
    var enumerator = enumerable.GetEnumerator();
    bool res = enumerator.MoveNext();
    // do whatever you want with res/enumerator.Current
    
    var enumerable2 = new EnumerableFromStartedEnumerator<T>(enumerator, res, enumerable);
    

    Now, the first GetEnumerator() that will be requested to enumerable2 will be given through the enumerator enumerator. From the second onward the enumerable.GetEnumerator() will be used.

    0 讨论(0)
  • 2020-12-30 19:21

    You could use the following which will kinda work.

    public class FakeEnumerable<T> : IEnumerable<T> {
      private IEnumerator<T> m_enumerator;
      public FakeEnumerable(IEnumerator<T> e) {
        m_enumerator = e;
      }
      public IEnumerator<T> GetEnumerator() { 
        return m_enumerator;
      }
      // Rest omitted 
    }
    

    This will get you into trouble though when people expect successive calls to GetEnumerator to return different enumerators vs. the same one. But if it's a one time only use in a very constrained scenario, this could unblock you.

    I do suggest though you try and not do this because I think eventually it will come back to haunt you.

    A safer option is along the lines Jonathan suggested. You can expend the enumerator and create a List<T> of the remaining items.

    public static List<T> SaveRest<T>(this IEnumerator<T> e) {
      var list = new List<T>();
      while ( e.MoveNext() ) {
        list.Add(e.Current);
      }
      return list;
    }
    
    0 讨论(0)
提交回复
热议问题