固定缓冲区和使用`MarshalAs` attr与`UnmanagedType.ByValArray`之间有什么区别?

时间:2014-07-29 08:55:51

标签: c# pinvoke

嗯,有一些明显的区别:
固定缓冲区:

  • 必须在不安全的块中声明它们(暗示整个项目必须使用-unsafe开关编译)。
  • 使用固定缓冲区时,必须修复包含对象

使用MarshalAs属性:

  • 虽然大小是给编组人员的,但是没有保证实际的数组有足够的元素数,也不保证它不是空的。
  • 该阵列可以像任何其他阵列一样简单直观地使用。

但是我找不到答案,为什么首先需要固定缓冲区呢? 什么时候必须使用它们? 为什么人们会想要使用它,假设一个人可以验证常规托管数组的大小?

我可以想到性能限制,可能会让人选择固定缓冲区而不是常规数组... 这就是全部?

提前致谢。

1 个答案:

答案 0 :(得分:4)

是的,效率肯定是主要原因。当您应用UnmanagedType.ByValArray时,结构必须始终被封送。换句话说,CLR被强制创建结构的新副本,并使用来自托管结构的值对其进行初始化,因为结构的非托管布局是不同的。当您使用固定缓冲区时,可以避免这种情况,前提是结构的其他成员也是blittable。在这种情况下,CLR可以简单地传递指向结构的指针。当然快得多。

有一些互操作方案,必须使用固定大小的缓冲区。通常,当阵列成员未对齐时,这违反了.NET内存模型的原子性保证。或者声明一个union(字段彼此重叠)和CLR对象,以防止引用类型的字段与值类型的字段重叠。这与垃圾收集器不兼容,它无法可靠地检测到对象指针。在这种情况下,你会在运行时得到一个TypeLoadException。

这两种情况都是根本无法验证的。如果本机代码写回结构,则总是不安全的,当它写入数组末尾时会发生内存损坏。非常难以诊断。当您使用固定大小缓冲区时,显式使用 unsafe 关键字的需要仅适用于在C#代码中访问固定大小缓冲区时缺少索引检查。