using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace Vanara.Collections { /// /// Delegate that gets the next value in an enumeration and returns true or returns false to indicate there are no more items in the enumeration. /// /// The type of the item. /// The value, on success, of the next item. /// true if an item is returned, otherwise false. public delegate bool TryGetNext(out TItem value); /// /// Delegate that gets the next value in an enumeration and returns true or returns false to indicate there are no more items in the enumeration. /// /// The type of the enumerator object. /// The type of the item. /// The enumerator object. /// The value, on success, of the next item. /// true if an item is returned, otherwise false. public delegate bool TryGetNext(TIEnum enumObj, out TItem value); /// An implementation the interface that can iterate through next and reset methods. public class IEnumeratorFromNext : IEnumerator where TIEnum : class { /// The current item being iterated. protected TItem current; /// The object that is enumerated. protected TIEnum ienum; /// The next function delegate. protected TryGetNext next; /// The reset function delegate. protected Action reset; /// Initializes a new instance of the class. /// The object to be enumerated. /// The method used to get the next value. /// The method used to reset the enumeration. /// Thrown if any parameter is . public IEnumeratorFromNext(TIEnum enumObj, TryGetNext next, Action reset) { if (enumObj == null || next == null || reset == null) throw new ArgumentNullException(); ienum = enumObj; this.next = next; this.reset = reset; reset(ienum); } /// /// Gets the object in the collection to which the enumerator is pointing. /// public virtual TItem Current => current; /// /// Gets the object in the collection to which the enumerator is pointing. /// object IEnumerator.Current => Current; /// Disposes of the Enumerator object. public virtual void Dispose() { ienum = null; current = default; } /// Moves the enumerator index to the next object in the collection. /// public virtual bool MoveNext() { try { return next(ienum, out current); } catch { return false; } } /// Resets the enumerator index to the beginning of the collection. public virtual void Reset() { current = default; reset(ienum); } } /// /// Creates an enumerable class from a counter and an indexer. Useful if a class doesn't support or /// like some COM objects. /// /// The type of the item. public class IEnumFromIndexer : IReadOnlyList { private readonly Func getCount; private readonly Func indexer; private readonly uint startIndex; /// Initializes a new instance of the class. /// The method used to get the total count of items. /// The method used to get a single item. /// The index at which the collection begins (usually 1 or 0). public IEnumFromIndexer(Func getCount, Func indexer, uint startIndex = 0) { if (getCount == null || indexer == null) throw new ArgumentNullException(); this.getCount = getCount; this.indexer = indexer; this.startIndex = startIndex; } /// Gets the number of elements in the collection. /// The number of elements in the collection. public int Count => (int)getCount(); /// Gets the item at the specified zero-based index. /// The item. /// The zero-based index. /// The item at the specified zero-based index. public TItem this[int index] => indexer((uint)index + startIndex); /// Returns an enumerator that iterates through the collection. /// A that can be used to iterate through the collection. public IEnumerator GetEnumerator() => new Enumerator(this); /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. [ExcludeFromCodeCoverage] IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); private class Enumerator : IEnumerator { private uint i; private IEnumFromIndexer ienum; public Enumerator(IEnumFromIndexer ienum) { this.ienum = ienum; ((IEnumerator)this).Reset(); } TItem IEnumerator.Current => ienum == null ? default : ienum.indexer(i); [ExcludeFromCodeCoverage] object IEnumerator.Current => ((IEnumerator)this).Current; void IDisposable.Dispose() => ienum = null; bool IEnumerator.MoveNext() => ienum != null && ++i < ienum.getCount() + ienum.startIndex; void IEnumerator.Reset() { if (ienum != null) i = ienum.startIndex - 1; } } } /// /// Creates an enumerable class from a get next method and a reset method. Useful if a class doesn't support or /// like some COM objects. /// /// The type of the item. public class IEnumFromNext : IEnumerable { /// The next function delegate. protected TryGetNext next; /// The reset function delegate. protected Action reset; /// Initializes a new instance of the class. /// The method used to try to get the next item in the enumeration. /// The method used to reset the enumeration to the first element. public IEnumFromNext(TryGetNext next, Action reset) { if (next == null || reset == null) throw new ArgumentNullException(); this.next = next; this.reset = reset; } /// Initializes a new instance of the class. protected IEnumFromNext() { } /// Returns an enumerator that iterates through the collection. /// A that can be used to iterate through the collection. public IEnumerator GetEnumerator() => new Enumerator(this); /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. [ExcludeFromCodeCoverage] IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); private class Enumerator : IEnumerator { private IEnumFromNext ienum; public Enumerator(IEnumFromNext ienum) { this.ienum = ienum; ((IEnumerator)this).Reset(); } public TItem Current { get; private set; } [ExcludeFromCodeCoverage] object IEnumerator.Current => Current; void IDisposable.Dispose() => ienum = null; bool IEnumerator.MoveNext() { if (ienum == null || !ienum.next(out var p)) return false; Current = p; return true; } void IEnumerator.Reset() { ienum?.reset(); Current = default; } } } }