C # StructLayout. Явный вопрос - PullRequest
10 голосов
/ 25 июля 2009

Я пытаюсь понять, почему второй пример ниже работает без проблем, но первый пример дает мне исключение ниже. Мне кажется, что оба примера должны дать исключение на основе описания. Может ли кто-нибудь просветить меня?

Необработанное исключение: System.TypeLoadException: Не удалось загрузить тип 'StructTest.OuterType' из сборки 'StructTest, Version = 1.0.0.0, Culture = нейтральный, PublicKeyToken = null', посколькуон содержит поле объекта со смещением 0, которое неправильно выровнено или перекрыто необъектным полем.
at StructTest.Program.Main (String [] args) Нажмите любую клавишу для продолжения. ,.

Пример 1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace StructTest
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct InnerType
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
        char[] buffer;
    }

    [StructLayout(LayoutKind.Explicit)]
    struct OuterType
    {
        [FieldOffset(0)]
        int someValue;

        [FieldOffset(0)]
        InnerType someOtherValue;
    }

    class Program
    {
        static void Main(string[] args)
        {
            OuterType t = new OuterType();
            System.Console.WriteLine(t);
        }
    }
}

Пример 2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace StructTest
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct InnerType
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
        char[] buffer;
    }

    [StructLayout(LayoutKind.Explicit)]
    struct OuterType
    {
        [FieldOffset(4)]
        private int someValue;

        [FieldOffset(0)]
        InnerType someOtherValue;

    }

    class Program
    {
        static void Main(string[] args)
        {
            OuterType t = new OuterType();
            System.Console.WriteLine(t);
        }
    }
}

Ответы [ 2 ]

12 голосов
/ 25 июля 2009

Общеязыковая среда выполнения содержит верификатор, который гарантирует, что работающий код (проверяемый IL) не может повредить память в управляемой среде. Это мешает вам объявить такую ​​структуру, в которой поля перекрываются. По сути, ваша структура содержит два элемента данных. Одно целое число (которое составляет 4 байта) и собственное целое число (размер указателя). В 32-битном CLR, в котором вы, вероятно, выполняете свой код, char[] займет 4 байта, поэтому, если вы поместите целое число менее чем в четыре байта от начала структуры, у вас будут перекрывающиеся поля. Интересно отметить, что оба ваших фрагмента кода с ошибкой в ​​64-битной среде выполнения, так как размер указателя составляет 8 байт.

1 голос
/ 26 июля 2009

Я подумал, что отвечу решением, которое я использовал для создания союза - что и было моим первоначальным намерением. Я использовал небезопасную структуру и фиксированный массив, а затем использовал свойство для взаимодействия с фиксированным массивом. Я считаю, что это должно делать то, что я хочу.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace StructTest
{

    [StructLayout(LayoutKind.Explicit)]
    unsafe struct OuterType
    {
        private const int BUFFER_SIZE = 100;

        [FieldOffset(0)]
        private int transactionType;

        [FieldOffset(0)]
        private fixed byte writeBuffer[BUFFER_SIZE];

        public int TransactionType
        {
            get { return transactionType; }
            set { transactionType = value; }
        }

        public char[] WriteBuffer
        {
            set
            {
                char[] newBuffer = value;

                fixed (byte* b = writeBuffer)
                {
                    byte* bptr = b;
                    for (int i = 0; i < newBuffer.Length; i++)
                    {
                         *bptr++ = (byte) newBuffer[i];
                    }
                }
            }

            get
            {
                char[] newBuffer = new char[BUFFER_SIZE];

                fixed (byte* b = writeBuffer)
                {
                    byte* bptr = b;
                    for (int i = 0; i < newBuffer.Length; i++)
                    {
                        newBuffer[i] = (char) *bptr++;
                    }
                }

                return newBuffer;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            OuterType t = new OuterType();
            System.Console.WriteLine(t);
        }
    }
}
...