C# StructLayout Pack=?? для использования со значениями bool

В C# я создаю несколько различных структур, которые содержат 16 переменных типа bool. У меня будет несколько различных из этих структур, которые затем будут объединены с другими типами данных в более сложные структуры. Мне нужно, чтобы они обрабатывались как 2 байта в длину. В приведенном ниже коде переменная, созданная с типом CtrlWord1, при выполнении Marshal.SizeOf будет иметь длину 64 независимо от того, создается ли она со значением Pack 0, 1 или 2.

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
    public bool a1;
    public bool a2;
    public bool a3;
    public bool a4;
    public bool a5;
    public bool a6;
    public bool a7;
    public bool a8;
    public bool b1;
    public bool b2;
    public bool b3;
    public bool b4;
    public bool c1;
    public bool c2;
    public bool c3;
    public bool c4;
}

2 ответа

Хотя bool тип в C# имеет размер всего 1 байт (sizeof(bool) == 1), CLR по умолчанию использует его как неуправляемый BOOL тип. Это размер, который вы получаете, когда вы звоните Marshal.SizeOf,

BOOL является typedef в заголовках Windows SDK для int, что составляет 4 байта в размере. Зачем? Потому что эти заголовки были написаны для языка Си в то время, когда у этого языка не было первоклассного логического типа. Это происходит сейчас, но решения зафиксированы в камне по причинам обратной совместимости. Маршалы ЦПР bool печатает таким образом, чтобы быть совместимым с функциями Windows API, которые используют BOOL значения, поскольку взаимодействие с Windows API является наиболее распространенным использованием P/Invoke. (По той же причине, что соглашение о вызовах по умолчанию для подписей P / Invoke - это stdcall вместо cdecl.)

Чтобы сказать CLR относиться к вашему bools как 1-байтовые bools, а не 4-байтовые BOOLs, используйте MarshalAs атрибут К сожалению, вы должны использовать его 16 раз:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
    [MarshalAs(UnmanagedType.I1)]  // marshal as a 1-byte signed int, not a 4-byte BOOL
    public bool a1;

    // etc.
}

Это гарантирует, что ваша структура будет только 16 байтов.

Тем не менее, нет никакого магического атрибута для создания битового поля. Вы должны будете создать и управлять этим самостоятельно, используя Int32 тип. Или используйте BitArray тип.

Глорин Оукенфут сказал это гораздо лучше, чем мог, поэтому я просто процитирую его

Упаковка / верстка выполняется на уровне байтов. Это означает, что bool никогда не займет меньше байта, полагаясь исключительно на упаковку. Вам придется сделать что-то более сложное, например, использовать два закрытых байтовых поля и несколько свойств, которые ссылаются на соответствующие биты в этих байтах.

Вот реализация этого, каждый элемент, который вы увеличиваете с правой стороны 1 << _ перейти к следующему битовому полю.

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CtrlWord1
{
    private Int16 _backingField;

    private void SetBitfield(Int16 mask, bool value)
    {
        if (value)
        {
            _backingField = (Int16)(_backingField | mask);
        }
        else
        {
            _backingField = (Int16)(_backingField & ~mask);
        }
    }

    private bool GetBitfield(Int16 mask)
    {
        return (_backingField & A1_MASK) != 0;
    }

    private const Int16 A1_MASK = 1 << 0;
    public bool a1
    {
        get { return GetBitfield(A1_MASK); }
        set { SetBitfield(A1_MASK, value); }
    }


    private const Int16 A2_MASK = 1 << 1;
    public bool a2
    {
        get { return GetBitfield(A2_MASK); }
        set { SetBitfield(A2_MASK, value); }
    }

    private const Int16 A3_MASK = 1 << 2;
    public bool a3
    {
        get { return GetBitfield(A3_MASK); }
        set { SetBitfield(A3_MASK, value); }
    }

    private const Int16 A4_MASK = 1 << 3;
    public bool a4
    {
        get { return GetBitfield(A4_MASK); }
        set { SetBitfield(A4_MASK, value); }
    }

    //And so on
}
Другие вопросы по тегам