包含结构体数组的c#结构体联合

本文关键字:结构体 数组 包含 | 更新日期: 2023-09-27 18:07:51

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

[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;
}

如果我单独使用apple结构,则封送工作得很好。但是,如果我尝试这样做;

[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' from assembly 'Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null',因为它包含一个偏移量为0的对象字段,该对象字段不正确对齐或被非对象字段重叠。

包含结构体数组的c#结构体联合

MarshalAsFieldOffsetStructLayout有本质区别。MarshalAs只是一个"正常"参数,指示编组器如何编组字段,它修改结构的非托管布局。另一方面,当你在c#中使用FieldOffset时,它会直接修改结构体的托管布局。MarshalAs对托管环境中的结构布局没有影响。因此,它不会使Types固定大小的值数组,因此CLR仍然会抱怨引用与值重叠(包含在相同偏移量的其他结构体之一中)。

对于基本类型,您可以使用fixed,但恐怕Type不可用。我猜,您需要为"数组"的每个元素创建一个具有15个物理字段的结构。不要忘记,只有当Types是struct(或enum)而不是引用时,它才会起作用。

但是,这通常不是常见问题的解决方案,只有在p/Invoke中。

问题是数组是引用类型(MarshalAs不会改变这一点-它仅在执行pinvoke时适用)。这意味着在结构体中有一个对其他地方数组的引用。在这种情况下,存储引用的内存位置与联合中的其他内容共享,这实际上是无法工作的。

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

唯一的限制是数组类型必须是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。虽然您必须执行三次,但这可能是一个更实际的选择。