Объединения структур 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 является структурой (или перечислением), а не ссылкой.

Тем не менее, это обычно не решение общей проблемы, только в P/Invoke.

person IS4    schedule 12.08.2015
comment
Итак, что я слышу, так это то, что, поскольку Types не является примитивным временем, мне придется создать 15 экземпляров, чтобы выполнить объединение. - person J. Morgan; 13.08.2015
comment
@ Дж. Морган Точно. В статье, на которую ссылается Крис, вы можете видеть, что в фиксированных массивах разрешены только примитивные типы (хотя я уверен, что 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