封装Span<Range>.Enumerator时抛出IndexOutOfRangeException问题
问题原因及解决办法
核心问题:只读值类型字段导致状态无法更新
Span<Range>.Enumerator是值类型(ref struct本质属于值类型范畴),而你把封装的枚举器字段标记为readonly。当调用MoveNext()时,因为字段是只读的,CLR会创建一个该枚举器的不可变副本执行方法——也就是说,你调用的MoveNext()其实是在副本上修改状态,原字段的枚举器根本没移动过。
这就导致了诡异的现象:MoveNext()返回true(副本的状态确实移动了),但你访问的是原只读字段的Current,而原枚举器还停留在初始无效状态,此时访问Current必然抛出IndexOutOfRangeException。
解决办法:移除readonly修饰符
只需要去掉_enumerator字段的readonly标记,让方法可以直接修改枚举器实例的状态:
public ref struct BasicEnumerator { // 移除readonly修饰符 private Span<Range>.Enumerator _enumerator; public Range Current => _enumerator.Current; public BasicEnumerator(Span<Range>.Enumerator enumerator) { _enumerator = enumerator; } public bool MoveNext() => _enumerator.MoveNext(); public BasicEnumerator GetEnumerator() => this; }
补充说明
直接使用Span<Range>.Enumerator时不会出问题,是因为此时枚举器是可变的局部变量,调用MoveNext()会直接修改自身状态,不会产生副本。而封装成只读字段后,值类型的拷贝特性加上只读限制,就触发了这个隐藏的坑。
内容的提问来源于stack exchange,提问作者killian-dtn
相关产品推荐
相关产品推荐

