因为需要在 C# 中处理类似 C 中的 Union 的数据结构,所以想通过显式指定结构体中字段的偏移量解决问题,结果却遇到了奇怪的问题
1. 结构体定义#
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(2)]
public byte[] Bytes;
}2. 异常信息:#
TypeLoadException:
未能从程序集“...”中加载类型“...MyStruct”,
因为它在 2 偏移位置处包含一个对象字段,
该字段已由一个非对象字段不正确地对齐或重叠。经过测试,数组类型的偏移量为4的倍数就不会报错,所以它是按4字节对齐的吗?可是已经指定 Pack = 1 了啊。
3. 问题原因#
感谢 @khm1600
原始回答:
![]()
参考 信息技术 - 公共语言基础结构 (CLI) 分区 I 到 VI
3.1 FieldLayout#
定位到标准文件:II.22.16 FieldLayout : 0x10

@khm1600 在评论中前半句所指应该是图二第七条,但是后半句关于 uint nint 是 4字节 对齐的说法见内存模型高亮部分
3.2 关于nint#
3.2.1 CLI支持的数据类型#
定位到标准文件: I.12.1 Supported data types
可以看到图三中 DataType 这列有 native int,描述为本机大小的二进制有符号值
3.2.2 本机类型大小#


PS:标准文件中搜索 nint 并没有搜到结果,但是在整型数值类型 - C# 参考中找到了相应的类型/关键字
平时基本上使用 C# 7.0 语法,好多新东西没学了😥
3.2.3 内存模型#
定位到标准文件 I.12.6 Memory model and optimizations
图五选中部分解释了本机类型大小的自然对齐规则,即在 32位进程 为 4 字节,64位进程 为 8 字节
4. 示例代码#
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct MyStruct
{
public byte A;
public short B;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] C;
}
//在解决方案平台 x64 下运行会出错,需要将C的Offset改为8
[StructLayout(LayoutKind.Explicit, Pack = 2)]
public struct MyStruct2
{
[FieldOffset(0)] public byte A;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(4)] public byte[] C;
[FieldOffset(1)] public short B;
}
[StructLayout(LayoutKind.Explicit, Pack = 2)]
public struct MyStruct3
{
[FieldOffset(0)] public byte A;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(8)] public byte[] C;
[FieldOffset(18)] public short B;
}
//测试值
A = 1,
B = 2,
C = new byte[10] { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 },


