using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace Vanara.Collections { /// /// 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 { private readonly TryGetNext next; private readonly 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; } /// /// 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 value, on success, of the next item. /// true if an item is returned, otherwise false. public delegate bool TryGetNext(out TItem value); /// 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() { if (ienum == null) return; ienum.reset(); Current = default; } } } }