给定对结构的托管引用,如何获取对偏移量字段的托管引用?
Given a managed reference to a struct, how to obtain a managed reference to a field at offset?
我正在尝试在 C# 中实现固定大小的数组,这些数组由 hacky 类型级数字参数化。参见 GitHub
我想实现一个方法,它将像这样工作:
static ref T Ref(this ref TContainer container, int index)
where T: unmanaged
where TContainer: unmanaged
=> ref container[index];
现在的问题是:我知道,TContainer
内部由 N 个 T
类型的字段组成。但它实际上并没有实现索引操作(或任何接口)。
我能做的是:
CheckIndex(index);
fixed (TContainer* self = &container) {
T* data = (T*)self;
return ref data[index];
}
这与 unsafe
一起编译,但我有两个问题:
- 像这样检索到的托管引用的生命周期是否会被正确跟踪?
- 在这种情况下使用
fixed
是否有任何性能成本? (例如 GC 固定、别名问题等)
此外,即使我将参数类型 ref TContainer
替换为 TContainer
,上面带有 fixed
和 return ref
的示例也会编译,或者,这应该是同样的,只是将其声明为实例成员而不是扩展方法。它甚至可以在示例测试用例中工作,但我不明白为什么会这样,因为在正常情况下,结构实例方法不能 return ref this.field
。
回答您的问题:
- 是的,因为您在固定时将非托管指针重建回托管指针;托管指针在任何时候都不能无效
- 不,
fixed
使用 very-low 开销机制 - 基本上,fixed
只是将局部变量标记为具有特殊含义;当 GC 运行时,它 已经 遍历 stack-frames,当它这样做时,它会查找这些局部变量并注意它们的值应被视为固定 [=29] =]
但是!听起来您真正追求的概念是 Span<T>
(或 Memory<T>
)- Span<T>
基本上是使用托管指针的 "range"。
这是一个不使用 unsafe
的版本:
public static ref T Ref<T, TContainer>(this ref TContainer container, int index)
where TContainer : unmanaged
where T : unmanaged
{
if (index < 0 || Unsafe.SizeOf<T>() * (index + 1) > Unsafe.SizeOf<TContainer>())
throw new ArgumentOutOfRangeException(nameof(index));
ref T first = ref Unsafe.As<TContainer, T>(ref container);
return ref Unsafe.Add<T>(ref first, index);
}
请注意,它仍然是 "unsafe"。
我正在尝试在 C# 中实现固定大小的数组,这些数组由 hacky 类型级数字参数化。参见 GitHub
我想实现一个方法,它将像这样工作:
static ref T Ref(this ref TContainer container, int index)
where T: unmanaged
where TContainer: unmanaged
=> ref container[index];
现在的问题是:我知道,TContainer
内部由 N 个 T
类型的字段组成。但它实际上并没有实现索引操作(或任何接口)。
我能做的是:
CheckIndex(index);
fixed (TContainer* self = &container) {
T* data = (T*)self;
return ref data[index];
}
这与 unsafe
一起编译,但我有两个问题:
- 像这样检索到的托管引用的生命周期是否会被正确跟踪?
- 在这种情况下使用
fixed
是否有任何性能成本? (例如 GC 固定、别名问题等)
此外,即使我将参数类型 ref TContainer
替换为 TContainer
,上面带有 fixed
和 return ref
的示例也会编译,或者,这应该是同样的,只是将其声明为实例成员而不是扩展方法。它甚至可以在示例测试用例中工作,但我不明白为什么会这样,因为在正常情况下,结构实例方法不能 return ref this.field
。
回答您的问题:
- 是的,因为您在固定时将非托管指针重建回托管指针;托管指针在任何时候都不能无效
- 不,
fixed
使用 very-low 开销机制 - 基本上,fixed
只是将局部变量标记为具有特殊含义;当 GC 运行时,它 已经 遍历 stack-frames,当它这样做时,它会查找这些局部变量并注意它们的值应被视为固定 [=29] =]
但是!听起来您真正追求的概念是 Span<T>
(或 Memory<T>
)- Span<T>
基本上是使用托管指针的 "range"。
这是一个不使用 unsafe
的版本:
public static ref T Ref<T, TContainer>(this ref TContainer container, int index)
where TContainer : unmanaged
where T : unmanaged
{
if (index < 0 || Unsafe.SizeOf<T>() * (index + 1) > Unsafe.SizeOf<TContainer>())
throw new ArgumentOutOfRangeException(nameof(index));
ref T first = ref Unsafe.As<TContainer, T>(ref container);
return ref Unsafe.Add<T>(ref first, index);
}
请注意,它仍然是 "unsafe"。