2017-10-13 95 views
0

在下面的struct中,我使用函数来跟随策略模式。为什么struct在使用函数时不会改变?

这是一个简单的范围枚举器。 如果通过否定长度,则会反向枚举。

它如何不按预期工作。当_move呼叫返回时,Position保持不变。

我想我知道原因,因为结构被复制到某个地方。但我似乎无法找到复制的地方。

(使用类,而不是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

问题在这里。

_move = length >= 0 ? (Func<bool>) this.MoveNextImpl : this.MovePrevImpl; 

在第一种观点,似乎你正在使用的将当前值实际上行事方法组。但事实并非如此。编译器是无声当使用实例方法组制作结构的副本。

我发现你不能在匿名方法,委托或lambda表达式中使用thisthe reason is explained here.

您必须将this复制到局部变量中,然后在lambda中使用该局部变量。使用实例方法组时,同样的事情正在发生,但默默地。如果编译器会在这里发出警告,那将会很好。

无论如何,解决方案是使用静态方法组。如果有更好的解决方案,我想知道。 (使用类而不是结构不是我正在寻找的答案。)

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() 
    { 
    } 
} 
相关问题