包含结构体数组的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的对象字段,该对象字段不正确对齐或被非对象字段重叠。
MarshalAs
与FieldOffset
或StructLayout
有本质区别。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
。虽然您必须执行三次,但这可能是一个更实际的选择。