尝试编组此结构时出现异常
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct Data
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U1)]
[FieldOffset(0x1)]
public byte[] a2;
}
它说 “无法从程序集'WTF,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'加载类型'WTF.Data',因为它包含偏移1处的对象字段,该字段未正确对齐或由非对象字段重叠。“
当我将偏移量1更改为0或4时,一切正常。 我做错了什么?
由于
答案 0 :(得分:4)
[StructLayout]会影响结构的托管和封送布局。 .NET中的一点怪癖但创建blittable结构是互操作的一个相当大的胜利,CLR无法忽略托管代码总是在完全不受管理的操作系统上运行的事实。不必创建结构的副本,只是能够将指针传递给托管版本是一个非常重要的性能。
您的[FieldOffset]值违反了.NET内存模型的强大保证,对象引用分配始终是原子的。一个昂贵的单词意味着另一个线程永远不会观察到仅部分更新的无效对象引用。原子性要求正确对齐,在32位模式下为4的倍数,在64位模式下为8。它们未对准,然后处理器可能需要执行多个存储器总线周期以将字节粘合在一起。这很糟糕,当另一个线程也在更新变量时,它会导致撕裂。从旧值中获取指针值的一部分,从新值中获取部分值。剩下的是一个破坏垃圾收集器的损坏指针。非常糟糕。
从C#的高级角度来看隐藏的东西,然而提供基本执行保证非常重要。只要您使用LayoutKind.Explicit,就不能将它错位为1,没有解决方法。所以不要使用它。
答案 1 :(得分:3)
请先看看Hans Passant的回答 - 对齐数据是一件好事,CLR强制执行它是有原因的。如果你出于某种原因真的需要或想要:
,它似乎有可能“作弊”[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public struct Data
{
public byte Dummy;
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 4,
ArraySubType = UnmanagedType.U1 )]
public byte[] a2;
}
也可以使用unsafe
代码:
[StructLayout( LayoutKind.Explicit, Pack = 1 )]
public unsafe struct Data
{
[FieldOffset( 1 )]
public fixed byte a2[4];
}
但同样,可能不是一个好主意。
编辑:第三个选项是简单地使数组长5个字节并使其偏移0,然后忽略第一个字节。这似乎更安全。