In following struct, I'm using function to follow strategy pattern.
this is a simple range enumerator. if negative length is passed, it will enumerate reversely.
How ever it does not work as expected. when _move
call is returned, Position
remains unchanged.
I guess I know the reason, its because struct is being copied somewhere. but I cant seem to find where copy is being made.
(using class instead of struct is not answer that I'm looking for.)
internal struct RangeEnumerator<T> : IEnumerator<T>
{
private readonly Func<bool> _move;
private readonly IReadOnlyList<T> _source;
private readonly int _start;
private readonly int _end;
// position of enumerator. not actual index. negative if reversed
public int Position { get; private set; }
public RangeEnumerator(IReadOnlyList<T> source, int start, int length)
{
start = Math.Min(Math.Max(start, 0), source.Count);
_source = source;
_start = start;
_end = Math.Min(Math.Max(length + start, 0), source.Count);
Position = -Math.Sign(length);
_move = null;
_move = length >= 0 ? (Func<bool>) this.MoveNextImpl : this.MovePrevImpl;
}
public bool MoveNext() => _move();
public void Reset() => Position = -1;
public T Current => _source[Position + _start];
object IEnumerator.Current => Current;
private bool MoveNextImpl() => ++Position + _start < _end;
private bool MovePrevImpl() => --Position + _start >= _end;
void IDisposable.Dispose()
{
}
}
Testing: for quick test, use the following code and debug.
public static class Program
{
public static void Main(string[] args)
{
var list = new List<int> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
var enumerator = new RangeEnumerator<int>(list, 3, 5); // 3 to 8 exclusive
foreach (var x in enumerator.AsEnumerable(0))
{
Console.WriteLine(x);
}
}
}
internal static class EnumeratorExtensions
{
public static StructEnumerable<TEnumerator, T> AsEnumerable<TEnumerator, T>(this TEnumerator enumerator, T _) where TEnumerator : struct, IEnumerator<T>
{
// struct copy doesn't matter since we didn't start enumerating yet.
return new StructEnumerable<TEnumerator, T>(enumerator);
}
}
// Enumerable to be used by foreach.
internal struct StructEnumerable<TEnumerator, T> where TEnumerator : struct, IEnumerator<T>
{
private TEnumerator _enumerator;
public StructEnumerable(TEnumerator enumerator)
{
// struct copy doesn't matter since we didn't start enumerating yet.
_enumerator = enumerator;
}
public TEnumerator GetEnumerator()
{
// struct copy doesn't matter since we didn't start enumerating yet.
return _enumerator;
}
}