在以下结构中,我使用函数来遵循策略模式。
这是一个简单的范围枚举器。 如果传递负长度,它将反向枚举。
它怎么没有按预期工作。返回_move
时,Position
保持不变。
我想我知道原因,因为struct正在被复制到某个地方。但我似乎无法找到正在制作副本的地方。
(使用class而不是struct不是我正在寻找的答案。)
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()
{
}
}
测试:进行快速测试,请使用以下代码并进行调试。
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;
}
}
答案 0 :(得分:0)
问题出在这里。
_move = length >= 0 ? (Func<bool>) this.MoveNextImpl : this.MovePrevImpl;
在第一个视图中,您似乎正在使用实际上会对当前值起作用的方法组。但它并没有。编译器 静默 在使用实例方法组时制作struct的副本。
我发现你无法在匿名方法,委托或lambda表达式中使用this
。 the reason is explained here.
您必须在本地变量中复制this
并在lambda中使用该局部变量。使用实例方法组时会发生同样的事情,但是会默默地发生。如果编译器会在这里发出警告会很好。
无论如何,解决方案是使用静态方法组。如果有更好的解决方案,我想知道。 (使用class而不是struct不是我正在寻找的答案。)
internal struct RangeEnumerator<T> : IEnumerator<T>
{
private readonly MoveStrategy _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;
// no this, therefor no copy
_move = length >= 0 ? (MoveStrategy)MoveNextImpl : MovePrevImpl;
}
public bool MoveNext() => _move(ref this);
public void Reset() => Position = -1;
public T Current => _source[Position + _start];
object IEnumerator.Current => Current;
private static bool MoveNextImpl(ref RangeEnumerator<T> v) => ++v.Position + v._start < v._end;
private static bool MovePrevImpl(ref RangeEnumerator<T> v) => --v.Position + v._start >= v._end;
private delegate bool MoveStrategy(ref RangeEnumerator<T> v);
void IDisposable.Dispose()
{
}
}