【问题标题】:Given a managed reference to a struct, how to obtain a managed reference to a field at offset?给定对结构的托管引用,如何在偏移量处获取对字段的托管引用?
【发布时间】:2019-10-15 19:32:32
【问题描述】:

我正在尝试在 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 编译,但我有两个问题:

  1. 是否会正确跟踪像这样检索到的托管引用的生命周期?
  2. 在这种情况下使用fixed 是否有任何性能成本? (例如 GC pinning、别名问题等)

此外,即使我将参数类型 ref TContainer 替换为 TContainer,或者应该相同,只需将其声明为实例成员,上面带有 fixedreturn ref 的示例也可以编译而不是扩展方法。它甚至可以在示例测试用例中使用,但我不明白为什么会这样,因为在正常情况下,无法从结构实例方法返回 ref this.field

【问题讨论】:

  • fixed 没有错误,它非常便宜,因为它实际上不需要固定。该变量具有 [pinned] 属性,如果 GC 恰好在此代码执行时收集,那么它会找到 [pinned] 并知道不移动对象。实际上是 ref data[] 会很痛苦。在对象内部生成一个内部指针,GC 必须遍历整个堆段才能找到对象。这个成本很难衡量,因为它只会增加“GC 时间百分比”的开销,而且如果 GC 在运行时运行,您会很不走运。

标签: c# return-by-reference c#-8.0


【解决方案1】:

回答您的问题:

  1. 是的,因为您将非托管指针重新构建回托管指针,同时将其固定;托管指针在任何时候都不能无效
  2. 不,fixed 使用非常低的开销机制 - 基本上,fixed 只是将局部变量标记为具有特殊含义;当 GC 运行时,它已经遍历堆栈帧,当它这样做时,它会查找这些本地变量并指出它们的值应该被视为固定

但是!听起来您真正追求的概念是Span<T>(或Memory<T>)-Span<T> 基本上是使用托管指针的“范围”。

这是一个不使用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 这里是System.Runtime.CompilerServices.Unsafe
  • 附加说明:就术语而言,这是“从值类型 TContainer 的托管引用中获取值类型 T 的内部指针”
  • 使用unsafeUnsafe 类有什么害处?
  • 我也不确定你在最后一句中引用的 unsafe 指的是什么。
  • @Lost Unsafe 中的许多方法实际上并不是“不安全的”——它们只是暴露了 IL 中可能发生的事情,但无法用 C# 表达。
猜你喜欢
  • 1970-01-01
  • 2012-05-14
  • 1970-01-01
  • 2012-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 2015-02-17
相关资源
最近更新 更多