C#包含结构阵列的结构的联合

时间:2015-08-12 21:39:58

标签: c# arrays struct marshalling unions

我正在尝试创建多个结构的联合。我遇到一个包含另一个结构数组的结构的问题。

[StructLayout(LayoutKind.Explicit)]
public struct FruitBasket
{
    [MarshalAs(UnmanagedType.Struct)]
    [FieldOffset(0)]
    public Apples Apple;

    [FieldOffset(0)]
    public Grapes Grape;

    [FieldOffset(0)]
    public Oranges Orange;
}

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi, Size = 12)]
public struct Apples
{
    public int Color;
    public int Texture;

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 15)]
    public Types[] Type;

}

如果我单独使用苹果结构,编组工作正常。但是,如果我尝试做类似的事情;

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi]
public class Buffet
{
    public UInt32 NumMeats;

    public UInt32 NumVeggies;

    public FruitBasket NumFruits;  //public Apples Apple; <-- works fine
}

我收到以下错误;

FruitBasket'来自程序集'Test,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null',因为它包含偏移0处的对象字段,该字段被非对象字段错误地对齐或重叠。

2 个答案:

答案 0 :(得分:0)

问题是数组是一种引用类型(MarshalAs没有改变它 - 它只适用于执行pinvoke时)。这意味着在结构中有一个引用到其他地方的数组。在您的情况下,存储该引用的内存位置与union中的其他内容共享,这可能无法正常工作。

所以你需要的是在结构中使用那个数组,如果使用不安全的代码,那就是possible。问题是:

  

唯一的限制是数组类型必须是bool,byte,char,short,int,long,sbyte,ushort,uint,ulong,float或double。

因此,您拥有Types - 它不是一个选项。但如果可以将其更改为其中一种受支持的类型,则可以这样做:

public unsafe struct Apples
{
    public int Color;
    public int Texture;

    public fixed int Type[15];
}

编辑:是的,正如@IllidanS4所提到的 - 如果你需要使用除这些类型之外的任何东西(结构),你可以手动添加这15个字段,逐个。不是很整洁......

编辑2:第二个选项是同时跳过联合 - 制作三个单独的结构并让编组人员使​​用Marshal.PtrToStructure为您排序。在这种情况下,MarshalAs将适用于数组,避免使用fixed。但是你必须做三次,但这可能是一个更实用的选择。

答案 1 :(得分:0)

MarshalAsFieldOffsetStructLayout有很大不同。 MarshalAs只是一个“普通”参数,它向Marshaller指示如何编组字段,它修改了结构的非托管布局。另一方面,当您在C#中使用它时,FieldOffset直接修改结构的托管布局。 MarshalAs对托管环境中的结构布局没有影响。因此,它不会使Types固定大小的值数组,因此CLR仍会抱怨引用与某个值重叠(包含在同一偏移量的其他结构中)。

对于原始类型,您可以使用fixed,但我担心它不适用于Type。我想,你可以为“数组”的每个元素创建一个包含15个物理字段的结构。不要忘记它仅在Types是结构(或枚举)而不是引用时才有效。

尽管如此,这通常不是常见问题的解决方案,只能在P / Invoke中使用。