固定大小的缓冲区不能直接从“this”对象使用

时间:2011-05-15 23:31:06

标签: c# this fixed

我用一种结构来表示纯数据。其中一个字段是固定大小的缓冲区,如下所示。

[StructLayout(LayoutKind.Sequential, Pack=2)]
unsafe struct ImageDosHeader
{
    ...
    private fixed ushort _e_res[4];
    ...

    [Description("Reserved")]
    [DisplayName("e_res[0]")]
    public ushort e_res_0 { get { ... } set { ... } }

    ...
}

在get / set函数中,我尝试执行以下操作但得到“编译器错误CS1666:您不能使用包含在未固定表达式中的固定大小缓冲区。请尝试使用fixed语句。”

return this._e_res[0];

然而,以下工作:

fixed (ImageDosHeader* p = &this)
    return p->_e_res[0];

ImageDosHeader local = this;
return local._e_res[0];

我可以轻松使用变通方法,但是,我想知道为什么直接从这里访问固定大小的缓冲区是非法的。或者这是我应该报告的错误吗?

我正在使用.NET 2.0。

2 个答案:

答案 0 :(得分:11)

这是因为IL指令的基础。

程序执行这一系列指令以获得所需的元素:

  1. 将地址加载到堆栈中。

  2. 将偏移量加载到堆栈上。

  3. 添加它们。

  4. 读取该内存地址的值。

  5. 如果对象在堆中,然后在步骤4之前因垃圾收集而移动,则从步骤1加载的地址将不再有效。为防止这种情况发生,您需要先将对象固定到内存中。

    (你通过this指针访问结构这一事实意味着你不知道结构是在堆上还是堆栈上,所以你必须把它固定好以防它开启堆。)

    第二个示例有效,因为 结构复制到堆栈,因此副本永远不会移动,因此地址始终有效。

    为什么其他类型的字段不会出现同样的问题?因为它们的偏移在编译时已知,而数组索引在运行时已知,因此JIT可以生成始终正确访问字段的代码。 / p>

答案 1 :(得分:5)

从中查看fixed关键字的视角会改变其语义,这是相当混乱的。 fixed声明的最初目的是固定存储器,在C#2.0中它与字段声明一起用来表示'数组正好是 N 元素长,因此,固定大小,不固定在内存中。

我在字段声明中删除了fixed关键字,只使用:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] private ushort[] _e_res;

这样,结构仍然是blittable,并不是一个痛苦的工作。