如何解决C# 是否强制“非托管”类型是“blittable”? 非托管类型约束文档Blittable 类型文档是否每个 unmanaged 类型都是“Blittable”?为什么我对此感到担忧“blittable”的定义一个类型是否“blittable”取决于对应代码的细节总结
我想知道 C# 7.3's Unmanaged type constraint 是否提供语言支持来强制类型是 blittable。根据 Blittable and Non-Blittable types 正确进行平台调用需要断言返回类型是 blittable:
从平台调用返回的结构必须是 blittable 类型。平台调用不支持非 blittable 结构作为返回类型。
然而,官方文档似乎没有说明 C# 强制 unmanaged
类型是“blittable”的任何地方。我对这样假设感到担忧,因为有证据表明,至少在某一时刻,一种类型是否“blittable”只能凭经验来确定。
非托管类型约束文档
The C# 7.3 "Unmanaged type constraint" proposal 包括以下内容:
非托管约束功能将强制执行 C# 语言规范中称为“非托管类型”的类型类。 ...
...
Blittable 与非托管
F# 语言有一个非常相似的特性,它使用关键字 unmanaged。 blittable 名称来自 Midori 中的使用。可能希望在此处查看优先级并改用非托管。
解决语言决定使用非托管
...
这里的短语“非托管类型”似乎指的是 Unsafe code - Pointer Types of the draft C# 6 specification 中定义的“unmanaged_type”(我在 the C# 5 ECMA standard 中没有找到类似的引用)。 unmanaged_type 定义如下:
非托管类型是任何类型,它不是 reference_type 或构造类型,并且不包含任何嵌套级别的 reference_type 或构造类型字段。换句话说,unmanaged_type 是以下之一:
-
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、char
、float
、double
、decimal
或bool
。 - 任何enum_type。
- 任何pointer_type。
- 任何用户定义的 struct_type 不是构造类型并且仅包含 unmanaged_types 字段。
我还没有发现interop marshaling documentation中提到的C# 7.3的unmanaged
类型约束;该文档似乎早于该功能。
Blittable 类型文档
Blittable and Non-blittable Types 声明以下内容是可 blittable 的:
-
System.Byte
、System.SByte
、system.int16
、System.UInt16
、system.int32
、System.UInt32
、system.int64
、System.UInt64
、system.intPtr
、System.UIntPtr
、System.Single
、System.Double
- blittable 类型的一维数组,例如整数数组。但是,包含 blittable 类型的可变数组的类型本身不是 blittable。
- 仅包含 blittable 类型的格式化值类型(如果它们被封送为格式化类型,则还包含类)。有关格式化值类型的详细信息,请参阅值类型的默认封送处理。
是否每个 unmanaged
类型都是“Blittable”?
起初我认为上面的定义意味着每个 unmanaged
类型都是“blittable”的。但我还没有找到任何直接出现并说明这一点的官方文件。在实现 unmanaged
类型约束功能之前,我发现的最接近的是 this reply(据我了解时间线):
这与互操作封送处理中的 Blittable 类型有什么关系?相同?或相似但略有不同?
是一样的。
这似乎是肯定的证据,但它也是作为设计决策基础的相当薄弱的证据。 (FWIW,这种交换也可以解释为所有“blittable”类型都是 unmanaged
,这似乎不太可能是真的。)
然后,根据文档,bool
不是“blittable”,System.Boolean
是 unmanaged
。那种复杂性is acknowledged but not directly addressed for the interop marshaler in the original proposal.
为什么我对此感到担忧
我不是一个活跃的 C# 开发人员,而这的历史似乎已经展开,而且肯定有我错过的线索。在我发现的线索中,很多都是否定的。似乎有一段时间 P/Invoke 的实现抵制了区分 blittable 和非 blittable 类型的总内涵定义。换句话说,至少在某些 C# 版本中,先验确认互操作封送拆收器是否认为一个类型是 blittable 是非常重要的(或者可能不可能)。或者至少看起来是这样。例如,Hans Passant wrote in 2015
没有简单的方法可以确定一个类型是否是 blittable,除了它不能正常工作或使用调试器和比较指针值。
另一个线索是 The fastest way to check if a type is blittable?(2012 年提出的问题)中的每个答案的方法似乎都是经验性的。这似乎是进一步否定的证据。
我想可能是所有 unmanaged
类型都是“blittable”,但并非所有“blittable”类型都是 unmanaged
。但这只是我的猜测。
解决方法
Hans Passant confirmed the answer in the negative.
不,最简单的反例是带有 bool 字段的结构体。永远不会 blittable,可以满足约束...
这似乎是得出答案是否定的结论的充分依据。
我认为汉斯回答的第二部分更有趣:
混合概念是没有意义的...
这与我解释其他似乎表明 unmanaged
和“blittable”是“相同”的意见的方式相反。我没有发现汉斯的任何详细说明,即混合 unmanaged
和“blittable”的概念是没有意义的。但我已经形成了自己的看法。
“blittable”的定义
术语“blittable”对于不同的人和在不同的上下文中(example 1、2)的含义似乎略有不同。我将在此答案的其余部分使用 here 中的以下定义:
Blittable 类型是在托管代码和本机代码中具有相同位级表示的类型。因此,它们无需转换为另一种格式即可在本机代码之间进行编组...
一个类型是否“blittable”取决于对应代码的细节。
根据上面的定义,类型是否是“blittable”取决于与您交换数据的对应代码是否以与您的代码相同的方式解释类型的位。 bool
和 BOOL
提供了一个很好的例子:
-
CLI specification 包含“布尔数据类型”的按位定义,如下所示:
- 在内存中占用 1 个字节
- 具有任何一个或多个位集的位模式表示为真
- 全零的位模式表示值为假
-
Win32 API 将
BOOL
定义为int
,而 MSVC'sint
为 32 位宽。 - .Net/.Net - 由于 CLI 对布尔数据类型的按位定义,例如,将
bool
的位从 .Net 写入文件并读取与 {{ 1}} 不需要转换;任何 .Net 程序都会以相同的方式解释此类布尔值的位。 - .Net/win32 - 由于 .Net
bool
和 win32 的bool
的按位表示之间的大小差异,在这些域之间交换布尔值需要转换。如果不进行转换,则发出BOOL
的 win32 API 只能设置最高有效 24 位中的位来表示真,而 .Net 很可能将其解释为BOOL
假。
总结
从 C# 的角度来看,一个类型是否是 blittable 取决于对应的代码是否恰好以相同的方式解释表示该类型的位。这将因交易对手而异。在 .Net 与 .Net 交易对手交换数据的情况下,bool
提供了一个约束,保证表示类型的位将被双方正确解释。
对于 .Net 以外的交易对手,目前不存在此类类似的语言限制。而且我不希望 C# 中存在这样的约束;交易对手很多,他们对代表其类型的位的解释肯定是以 C# 规范不能依赖的方式定义的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。