C# Обединения на структури, които съдържат масиви от структури

Опитвам се да създам обединение на множество структури. Имам проблем с една структура, която съдържа масив от друга структура.

[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" от сборка "Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", тъй като съдържа обектно поле при отместване 0, което е неправилно подравнено или припокрито от необектно поле.


person J. Morgan    schedule 12.08.2015    source източник
comment
Какво има в Grapes и Oranges? Освен това какво е Types?   -  person IS4    schedule 13.08.2015
comment
@IllidanS4 Grapes и Oranges също са структури, но съдържат само основни типове като Ints, Bools и т.н.   -  person J. Morgan    schedule 13.08.2015


Отговори (2)


MarshalAs е съществено различен от FieldOffset или StructLayout. MarshalAs е просто "нормален" параметър, който показва на Marshaller как да маршалира полето, той променя неуправляваното оформление на структурата. От друга страна, FieldOffset директно променя управляваното оформление на структурата, когато го използвате в C#. MarshalAs няма ефект върху структурното оформление в управляваната среда. Следователно, той няма да направи Types масив със стойности с фиксиран размер, така че CLR все още ще се оплаква от припокриване на препратка от стойност (съдържаща се в една от другите структури със същото отместване).

За примитивни типове можете да използвате fixed, но не е наличен за Type, страхувам се. Остава ви да създадете структура с 15 физически полета за всеки елемент от "масива", предполагам. Не забравяйте, че ще работи само ако Types е структура (или enum), а не препратка.

Все пак това обикновено не е решението за често срещан проблем, само в P/Invoke.

person IS4    schedule 12.08.2015
comment
Това, което чувам, е, че тъй като Types не е примитивно време, ще трябва да направя 15 екземпляра от него, за да направя обединението. - person J. Morgan; 13.08.2015
comment
@J.Morgan Точно така. В статията, свързана от Крис, можете да видите, че само примитивни типове са разрешени във фиксирани масиви (въпреки че съм сигурен, че CLR ще поддържа всички типове). Не забравяйте, че в крайна сметка ще имате 15 екземпляра на Types и в двете посоки. - person IS4; 13.08.2015

Проблемът е, че масивът е референтен тип (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. Все пак ще трябва да го направите три пъти, но това може да е по-практична алтернатива.

person Chris    schedule 12.08.2015