我们什么时候应该使用这个属性,为什么我们需要它?例如,如果 c 中的 native 函数将指向 unsigned char 的指针作为参数,并且我知道需要它来实现 unsigned chars 数组,为什么我不能在 C# 中使用字节数组来使用此函数?有必要做编组吗?
3 回答
在大多数情况下,运行时将能够自动确定如何在本机代码和托管代码之间编组数据,因此您通常不需要指定属性。MarshalAs
仅当定义中存在歧义时(并且您想准确地告诉运行时如何编组数据)或您需要非默认行为时才需要。
根据我的经验,MarshalAs
只有在处理字符串时才真正需要,因为本机代码中有很多不同的表示形式;unicode/ansi, c-strings 与否等。
MarshalAs 属性的其他用途是使用 ByValArray 和 SizeConst 参数编组固定大小的数组(包括固定大小的字符串)。例如,Windows API 中的许多结构都包含固定大小的字符串。
当类型需要在托管代码和本机代码之间交叉时,编组是转换类型的过程。需要编组是因为托管代码和非托管代码中的类型不同。例如,在托管代码中,您有一个字符串,而在非托管世界中,字符串可以是 Unicode(“宽”)、非 Unicode、空终止、ASCII 等。默认情况下,P/Invoke 子系统尝试执行正确的事情基于默认行为,如本文所述。但是,对于需要额外控制的情况,您可以使用 MarshalAs属性来指定非托管端的预期类型。
通常,运行时在编组时会尝试做“正确的事情”,以减少您的工作量。
以下从文档到Blittable 和 Non-Blittable Types的链接解释了哪些类型需要特殊处理:
大多数数据类型在托管和非托管内存中都有一个共同的表示,并且不需要互操作封送拆收器的特殊处理。这些类型称为 blittable 类型,因为它们在托管和非托管代码之间传递时不需要转换。
非 blittable类型将是您问题的答案。您必须为以下内容编组:
数组、布尔值、字符、类、对象、字符串、值类型(结构)、委托、非托管数组,它们是 COM 样式的安全数组或具有固定或可变长度的 C 样式数组。
非托管结构还可以包含嵌入式数组或布尔值(非 blittable 类型)。根据文档,您必须小心:
从平台调用调用返回的结构必须是 blittable 类型。平台调用不支持非 blittable 结构作为返回类型。