2016-08-31 4 views
0

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

[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; 
} 
+0

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

+0

Вы можете использовать [BitVector32] (https://msdn.microsoft.com/en-us/library/system.collections.specialized.bitvector32.aspx), чтобы получить до 32 бит. Или вы можете использовать технику, описанную в http://stackoverflow.com/questions/4107039/bitarray-alternative-for-the-net-micro-framework/4107287#, но используйте 'ushort', а не' long'. –

ответ

0

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

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

Честно CLR, чтобы относиться к своим bool с, как 1 байт BOOLS, а не 4 байта BOOL с, используйте MarshalAs attribute. К сожалению, вы должны использовать его 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.

+1

Это даст ему 16-байтовую структуру, он ищет 2-байтовое битовое поле. –

+0

Проблема с «BitArray» заключается в том, что она выделяет массив, который несет накладные расходы распределения (12 или 24 байта), плюс 20 или 30 байтов накладных расходов массива. –

0

Glorin Oakenfoot said it much better than I could, так что я просто процитирую его

Упаковка/макет делается на уровне байтов. Это означает, что 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 
}