我正在用这种类型尝试一些东西Span<T>
。我的目标是创建一个扩展方法来枚举 a 的拆分ReadOnlySpan<char>
,用法如下:
var x = "foo,bar,baz".AsSpan();
var options = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries;
foreach (var word in x.EnumerateSplit(',', options))
{
// Should enumerate foo, bar and baz
}
但是,当我实现自己的枚举器类型(在Span<Range>.Enumerator
后台封装实例)时,IndexOutOfRangeException
在访问封装的枚举器Current
属性时会抛出 an 。
我尝试创建最基本的枚举器,它只有封装逻辑,没有分割机制:
public ref struct BasicEnumerator
{
// Encapsulated enumerator
private readonly Span<Range>.Enumerator _enumerator;
public Range Current => _enumerator.Current;
public BasicEnumerator(Span<Range>.Enumerator enumerator)
{
_enumerator = enumerator;
}
public bool MoveNext() => _enumerator.MoveNext();
// Called by foreach
public BasicEnumerator GetEnumerator() => this;
}
对于这种BasicEnumerator
类型,我至少期望这段代码能够工作:
Span<Range> test = stackalloc Range[5];
var enumerator = new BasicEnumerator(test.GetEnumerator());
foreach (Range r in enumerator)
{
Console.WriteLine(r);
}
但IndexOutOfRangeException
在第一次迭代时抛出了 an 。
我还尝试了更简单的方法而不使用foreach
:
Span<Range> test = stackalloc Range[5];
var enumerator = new BasicEnumerator(test.GetEnumerator());
if (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
IndexOutOfRangeException
尝试访问时仍会抛出enumerator.Current
。
请注意,当我使用Span<Range>.Enumerator
(以相同的方式)代替时,它会起作用:
Span<Range> test = stackalloc Range[5];
Span<Range>.Enumerator enumerator = test.GetEnumerator();
if (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
在调试时,我发现它不起作用,因为该Span<Range>.Enumerator.MoveNext
方法不会增加底层索引,但仍然返回true
:
Visual Studio 调试屏幕截图
我真的不知道为什么要这样做。我想说这是一个参考问题,但我不知道为什么以及如何解决它。我试图找到有关类似用例的文档,但没有成功。
我正在使用.NET 8。
这是因为结构是值类型+你的
_enumerator
结构readonly
。因此,当您调用 时BasicEnumerator.MoveNext()
,您会获得内部结构的副本_enumerator
,然后调用MoveNext()
该副本的 ,然后丢弃该副本。原始结构保持不变,因此它Current
仍然是-1。要解决此问题,请删除只读限定符
错误就在这里
因为它是
readonly
,所以代码MoveNext
正在执行防御性访问时复制,这是正确的,以防止对底层结构进行任何更改。只需删除
readonly
它就可以了点网小提琴