using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace Chernobyl.Collections.Generic { /// /// An that continuously loops through an . /// public class LoopingEnumerator : IEnumerator { /// The values to infinitely loop. /// True if this instance should start at the end when it is finished /// with one full iteration. False if it should start at the beginning. public LoopingEnumerator(IEnumerable enumerable, bool reverseWhenFinished = false) { _enumerable = enumerable; _reverseWhenFinished = reverseWhenFinished; _forward = _enumerable.GetEnumerator(); _backward = _enumerable.Reverse().GetEnumerator(); _enumerator = _forward; } /// public T Current { get { if (_currentAvailable == null) throw new InvalidOperationException("Enumeration has not started. Call MoveNext."); var current = default(T); if (_currentAvailable == true) current = _enumerator.Current; return current; } } /// object IEnumerator.Current => Current; /// public bool MoveNext() { bool result = _enumerator.MoveNext(); _currentAvailable = result; if (result == false) { if(_reverseWhenFinished) { if (_enumerator == _forward) { _enumerator.Reset(); _enumerator = _backward; } else { // Reverse enumerators don't support Reset so we have to grab a fresh // reverse enumerator. _enumerator = _forward; _backward.Dispose(); _backward = _enumerable.Reverse().GetEnumerator(); } } else _enumerator.Reset(); _currentAvailable = result = _enumerator.MoveNext(); } return result; } /// public void Reset() { _enumerator.Reset(); _enumerator = _forward; } /// public void Dispose() { _backward.Dispose(); _forward.Dispose(); } readonly IEnumerable _enumerable; readonly bool _reverseWhenFinished; readonly IEnumerator _forward; IEnumerator _backward; IEnumerator _enumerator; // Set to the last return value of MoveNext or null if MoveNext has not been called. bool? _currentAvailable; } /// /// Utility and extension methods for . /// public static class LoopingEnumerator { /// /// Turns an into an infinitely looping . /// /// The values to infinitely loop. /// True if this instance should start at the end when it /// is finished with one full iteration. False if it should start at the beginning. public static IEnumerable AsLooping(this IEnumerable enumerable, bool reverseWhenFinished = false) => new EnumeratorEnumerable(() => new LoopingEnumerator(enumerable, reverseWhenFinished)); } }