编组 LayoutKind.具有重叠的显式结构在发布版本中失败

本文关键字:布版本 版本 失败 结构 LayoutKind 重叠 编组 | 更新日期: 2023-09-27 18:33:12

我有一个结构,它有一个报告为重叠的非重叠字段。

[FieldOffset(8)]
Int32 X;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
[FieldOffset(12)]
string Y;
[FieldOffset(28)]
int Z;

报告的错误是:

无法加载类型"XXX"...它包含偏移量 12 处的对象字段,该字段被非对象字段错误地对齐或重叠。

它只发生在发布配置中(TRACE、DEBUG 标志和不安全代码已启用,优化已关闭),猜测 - 会发生什么?

UPD:感谢@svick。确认 x64 构建不是人们想要的编组。

编组 LayoutKind.具有重叠的显式结构在发布版本中失败

首先,发布配置与此无关。影响它的是平台目标:如果将其设置为 x64,则会出现此异常,但如果将其设置为 x86,它将正常工作。

我认为这种行为的原因是FieldOffset用于指定托管内存中struct的布局(即使文档没有说明这一点),但MarshalAs不在托管内存中使用。

因此,托管内存中的对象在偏移量 12 处包含引用。由于所有引用都必须在 .Net 中对齐(在 32 位应用程序中对齐到 4 个字节,在 64 位应用程序中对齐到 8 个字节),因此如果以 64 位运行应用程序,则会出现异常。

因此,问题不在于字段重叠,而在于错误消息的另一部分:字段对齐不正确。

简单的解决方法是将应用程序编译为 x86。如果您无法做到这一点,我不确定如何解决此问题。

注释@svick的正确答案,这里的问题是您的结构声明违反了 CLR 使对象赋值是原子的硬承诺。 这在 64 位模式下不起作用,偏移量为 12 的对象指针可以跨越缓存行的末尾。 访问这种未对齐的成员始终需要两次读取或写入,并且永远不可能是原子的。 我认为这实际上是 CLR 类型验证器中的一个错误,但这不会帮助您克服这个驼峰。

当然,您

这样做是为了与 32 位代码互操作,并且您正确更改了调试版本的平台目标设置,但忘记为发布版本执行此操作。 它是按配置设置的设置。 轻松修复,只需更改发布配置的设置即可。

如果您确实需要它在 64 位模式下工作,则需要将其声明为 fixed char[16]

我认为系统中数据字段的默认对齐方式为 8 字节。必须对 Y 使用偏移量 16。