我正在实现一个基于堆栈的向量类型,如下所示:
[StructLayout(LayoutKind.Sequential)]
public struct VectorI16x8 {
public short s0, s1, s2, s3, s4, s5, s6, s7;
public short this[int i] {
get => UnsafeValues[i];
set => UnsafeValues[i] = value;
}
public int Length => 8;
private Span<short> UnsafeValues => MemoryMarshal.CreateSpan(ref s0, Length);
}
很明显,如果我将
UnsafeValues
公开,则该结构体的值可能会在内存中移动(即使没有垃圾收集器),从而导致 Span
内出现悬空引用或导致 Span
指向不正确的实例。
但是,不太清楚的是在我的结构实现中私下使用这样的跨度是否安全。我的问题是:我是否可以假设我的结构的
this
在索引器执行期间不会移动(这意味着由 UnsafeValues
创建的跨度将保持有效),或者我是否必须使用 fixed
块来实现此目的安全吗?
您不需要为此使用
fixed
块;简而言之,GC 是“必需的”来处理这种情况。 Span 本质上与任何其他“内部指针”没有什么不同 - 并且相同的问题可以通过结构体中调用 DoSomething(ref s0)
、DoSomethingElse(in s1)
等的某些方法引发。这是 why托管指针的一部分 (
ref
、 Span<T>
等)只能存储在堆栈上,而不是作为类型上的字段(ref struct
除外) - 以便 GC 知道在哪里检查它们(它需要遍历堆栈帧 anyay,以检查
fixed
标记等 - 这些只是当地人的特殊标记)。 GC 是否通过决定不移动这些区域(就像它们是fixed
)或修复引用(在暂停期间)来处理此问题是 GC 的实现细节。