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;
}
}
}
}