File: System\Linq\Iterator.cs
Web Access
Project: src\src\libraries\System.Linq\src\System.Linq.csproj (System.Linq)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Collections.Generic;
 
namespace System.Linq
{
    public static partial class Enumerable
    {
        /// <summary>
        /// A base class for enumerables that are loaded on-demand.
        /// </summary>
        /// <typeparam name="TSource">The type of each item to yield.</typeparam>
        /// <remarks>
        /// <list type="bullet">
        /// <item><description>
        /// The value of an iterator is immutable; the operation it represents cannot be changed.
        /// </description></item>
        /// <item><description>
        /// However, an iterator also serves as its own enumerator, so the state of an iterator
        /// may change as it is being enumerated.
        /// </description></item>
        /// <item><description>
        /// Hence, state that is relevant to an iterator's value should be kept in readonly fields.
        /// State that is relevant to an iterator's enumeration (such as the currently yielded item)
        /// should be kept in non-readonly fields.
        /// </description></item>
        /// </list>
        /// </remarks>
        private abstract partial class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
        {
            private readonly int _threadId = Environment.CurrentManagedThreadId;
 
            private protected int _state;
            private protected TSource _current = default!;
 
            /// <summary>
            /// The item currently yielded by this iterator.
            /// </summary>
            public TSource Current => _current;
 
            /// <summary>
            /// Makes a shallow copy of this iterator.
            /// </summary>
            /// <remarks>
            /// This method is called if <see cref="GetEnumerator"/> is called more than once.
            /// </remarks>
            private protected abstract Iterator<TSource> Clone();
 
            /// <summary>
            /// Puts this iterator in a state whereby no further enumeration will take place.
            /// </summary>
            /// <remarks>
            /// Derived classes should override this method if necessary to clean up any
            /// mutable state they hold onto (for example, calling Dispose on other enumerators).
            /// </remarks>
            public virtual void Dispose()
            {
                _current = default!;
                _state = -1;
            }
 
            /// <summary>
            /// Gets the enumerator used to yield values from this iterator.
            /// </summary>
            /// <remarks>
            /// If <see cref="GetEnumerator"/> is called for the first time on the same thread
            /// that created this iterator, the result will be this iterator. Otherwise, the result
            /// will be a shallow copy of this iterator.
            /// </remarks>
            public Iterator<TSource> GetEnumerator()
            {
                Iterator<TSource> enumerator = _state == 0 && _threadId == Environment.CurrentManagedThreadId ? this : Clone();
                enumerator._state = 1;
                return enumerator;
            }
 
            /// <summary>
            /// Retrieves the next item in this iterator and yields it via <see cref="Current"/>.
            /// </summary>
            /// <returns><c>true</c> if there was another value to be yielded; otherwise, <c>false</c>.</returns>
            public abstract bool MoveNext();
 
            /// <summary>
            /// Returns an enumerable that maps each item in this iterator based on a selector.
            /// </summary>
            /// <typeparam name="TResult">The type of the mapped items.</typeparam>
            /// <param name="selector">The selector used to map each item.</param>
            public virtual IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) =>
#if OPTIMIZE_FOR_SIZE
                new IEnumerableSelectIterator<TSource, TResult>(this, selector);
#else
                new IteratorSelectIterator<TSource, TResult>(this, selector);
#endif
 
 
            /// <summary>
            /// Returns an enumerable that filters each item in this iterator based on a predicate.
            /// </summary>
            /// <param name="predicate">The predicate used to filter each item.</param>
            public virtual IEnumerable<TSource> Where(Func<TSource, bool> predicate) =>
                new IEnumerableWhereIterator<TSource>(this, predicate);
 
            object? IEnumerator.Current => Current;
 
            IEnumerator<TSource> IEnumerable<TSource>.GetEnumerator() => GetEnumerator();
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
            void IEnumerator.Reset() => ThrowHelper.ThrowNotSupportedException();
        }
    }
}