我遇到了一个奇怪的情况,即在C#/.NET中编组包含数组的联合体。考虑以下程序:
namespace Marshal
{
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct InnerType
{
byte Foo;
//[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
//byte[] Bar;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
struct UnionType
{
[FieldOffset(0)]
InnerType UnionMember1;
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
byte[] UnionMember2;
}
static void Main(string[] args)
{
Console.WriteLine(@"SizeOf UnionType: {0}", System.Runtime.InteropServices.Marshal.SizeOf(typeof(UnionType)));
}
}
}
如果您运行此程序,将会出现以下异常:
Could not load type 'UnionType' from assembly 'Marshal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.
如果您取消注释这两行代码,则程序将运行良好。我在想这是为什么。为什么将额外的数组添加到InnerType中可以解决问题?顺便说一下,无论您使数组多大都没有关系。如果没有数组,则UnionMember1和UnionMember2应该具有相同的大小。但是,如果有数组,则它们的大小不匹配,但不会引发任何异常。
更新:将InnerType更改为以下内容也会导致异常(这次是在InnerType上)。
[StructLayout(LayoutKind.Explicit, Pack = 1)]
struct InnerType
{
[FieldOffset(0)]
byte Foo;
[FieldOffset(1)]
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1)]
byte[] Bar;
}
我认为这应该等同于原始代码(使用
LayoutKind.Sequential
),其中未注释byte[] Bar
。我不认为这里的问题与单词边界有任何关系--我正在使用Pack = 1。相反,我认为异常的第二部分是问题所在:“...它包含一个偏移量为0的对象字段,该字段被非对象字段重叠。”byte[]是引用类型,而byte本身是值类型。我可以看到“byte Foo”最终会与“byte[] UnionMember2”重叠。然而,这仍然无法解释为什么取消注释原始代码中的“byte[] bar”会使异常消失。