固定缓冲区与使用“MarshalAs”属性和“UnmanagedType.ByValArray”之间有什么区别?

Shl*_*itz 5 c# pinvoke

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

  • 它们必须在 unsafe 块中声明(意味着整个项目必须使用-unsafeswitch 进行编译)。
  • 为了使用固定缓冲区,包含的对象必须是固定的,

使用MarshalAs属性:

  • 尽管大小已提供给编组器,但不能保证实际数组具有足够的元素计数,也不能保证它不为空。
  • 该数组可以像任何其他数组一样简单直观地使用。

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

我可以想到性能限制,这可能会让人们选择固定缓冲区而不是常规数组......
仅此而已吗?

提前致谢。

Han*_*ant 5

是的,效率当然是首要原因。当您应用时,必须始终UnmanagedType.ByValArray对结构进行编组。换句话说,由于结构的非托管布局不同,CLR 被迫创建该结构的新副本并使用托管结构中的值对其进行初始化。当您使用固定缓冲区时,可以避免这种情况,前提是结构体的其他成员也可以进行 blittable。在这种情况下,CLR 可以简单地传递一个指向该结构的指针。当然更快。

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

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