Если вы хотите поместить структуры в другие структуры, которые сами являются Layoutind.Explict, вам следует использовать явное значение размера (в байтах), если вы ожидаете, что они будут работать в разных режимах битности (или на машинах с различными требованиями к упаковке)
То, что вы говорите, это «выкладывайте вещи последовательно и не упаковывайте их внутренне, а используйте в конце столько места, сколько вам нужно».
Если вы не укажете Size, среда выполнения может добавить столько места, сколько пожелает.
Причина, по которой в общем случае отказывается допускать перекрытие структур и типов объектов, заключается в том, что процедура GC должна свободно проходить по графу живых объектов. При этом он не может знать, имеет ли значение объединенное (перекрывающееся) поле как ссылку на объект или как необработанные биты (скажем, int или float). Так как должен проходить все ссылки на живые объекты для правильного поведения, в конечном итоге он будет проходить «случайные» биты, которые могут указывать где-нибудь в куче (или вне ее), как если бы они были ссылками, прежде чем вы это узнаете повторная неисправность общей защиты.
Поскольку 32/64 ссылки будут занимать 32 или 64 бита в зависимости от времени выполнения, вы должны использовать Explict, только ссылки объединения со ссылками и типы значений с типами значений, убедитесь, что ваши ссылочные типы выровнены по границам обеих целевых платформ, если они отличаются (Примечание: время выполнения зависит от ниже) и выполняют одно из следующих действий:
- Убедитесь, что все ссылочные поля являются последней записью в структуре - тогда можно свободно увеличивать / уменьшать структуру в зависимости от разрядности среды выполнения.
- Заставить все ссылки на объекты потреблять 64 бита, независимо от того, находитесь ли вы в 32- или 64-битной среде
Примечание по выравниванию:
извинения
Я ошибся в невыровненных ссылочных полях - компилятор удалил загрузку типа, если я не выполнил какое-либо действие со структурой.
[StructLayout(LayoutKind.Explicit)]
public struct Foo
{
[FieldOffset(0)]
public byte padding;
[FieldOffset(1)]
public string InvalidReference;
}
public static void RunSnippet()
{
Foo foo;
foo.padding = 0;
foo.ValidReference = "blah";
// Console.WriteLine(foo); // uncomment this to fail
}
Соответствующие подробности содержатся в спецификации ECMA http://www.ecma -international.org / публикации / файлы / ECMA-ST / Ecma-335.pdf см. Раздел 16.6.2, в котором предписывается выравнивание исходного размера значения, включая &. В нем отмечается, что для приведения этого в соответствие требуется инструкция без выравнивания.
На моно (как OSX Intel, так и Win32 Intel 32 bit) вышеуказанный код работает. Либо среда выполнения не учитывает макеты и молча «корректирует» вещи, либо допускает произвольное выравнивание (исторически они были менее гибкими, чем среда выполнения MS в этом отношении, что удивительно).
Промежуточная форма CLI, генерируемая mono, не содержит префиксов инструкций .unaligned, так как она, как представляется, не соответствует спецификации.
Это научит меня проверять только моно.