简体   繁体   English

C# StructLayout Pack=?? 用于布尔值

[英]C# StructLayout Pack=?? for use with bool values

In C#, I am creating multiple different structs that contain 16 variables of type bool.在 C# 中,我创建了多个不同的结构,其中包含 16 个 bool 类型的变量。 I will have several different of these structs that will then be combined with other data types into more complex structs.我将有几个不同的这些结构体,然后将它们与其他数据类型组合成更复杂的结构体。 I am needing to have them treated as being 2 bytes in length.我需要将它们视为 2 个字节的长度。 In the code below, a variable created of type CtrlWord1 will give a length of 64 when I do a Marshal.SizeOf regardless of whether it is created with a Pack value of 0, 1 or 2.在下面的代码中,当我执行 Marshal.SizeOf 时,创建的 CtrlWord1 类型的变量的长度将为 64,无论它是使用 0、1 还是 2 的 Pack 值创建的。

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

Although the bool type in C# is only 1 byte in size ( sizeof(bool) == 1 ), the CLR defaults to marshalling it as the unmanaged BOOL type.尽管 C# 中的bool类型只有 1 个字节( sizeof(bool) == 1 ),但 CLR 默认将其编组为非托管BOOL类型。 This is the size you get when you call Marshal.SizeOf .这是您在调用Marshal.SizeOf时获得的大小。

BOOL is a typedef in the Windows SDK headers for an int , which is 4 bytes in size. BOOL是 Windows SDK 标头中int的 typedef,大小为 4 个字节。 Why?为什么? Because these headers were written for the C language at a time that this language did not have a first-class Boolean type.因为这些头文件是在 C 语言没有一流布尔类型的时候为 C 语言编写的。 It does now, but the decisions are fixed in stone for backwards-compatibility reasons.现在确实如此,但出于向后兼容性的原因,这些决定是一成不变的。 The CLR marshals bool types this way in order to be compatible with Windows API functions that use BOOL values, since interop with the Windows API is the most common usage of P/Invoke. CLR 以这种方式封送bool类型,以便与使用BOOL值的 Windows API 函数兼容,因为与 Windows API 的互操作是 P/Invoke 的最常见用法。 (The same reason that the default calling convention for P/Invoke signatures is stdcall instead of cdecl.) (与 P/Invoke 签名的默认调用约定是 stdcall 而不是 cdecl 的原因相同。)

To tell the CLR to treat your bool s as 1-byte bools, rather than 4-byte BOOL s, use the MarshalAs attribute .要告诉 CLR 将您的bool视为 1 字节bool而不是 4 字节BOOL ,请使用MarshalAs属性 Unfortunately, you have to use it 16 times:不幸的是,您必须使用它 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.
}

This will ensure that your struct is only 16 bytes.这将确保您的结构只有 16 个字节。

However, there's no magic attribute to generate a bitfield.但是,没有生成位域的魔法属性。 You'll have to create and manage this yourself using the Int32 type.您必须使用Int32类型自己创建和管理它。 Or use the BitArray type.或者使用BitArray类型。

Glorin Oakenfoot said it much better than I could , so I will just quote him Glorin Oakenfoot 说得比我好,所以我只引用他的话

Packing/layout is done at the byte level.打包/布局是在字节级别完成的。 That means a bool will never take less than a byte relying purely on packing.这意味着一个 bool 永远不会少于一个纯粹依赖于打包的字节。 You'll have to do something a little more involved, such as use two private byte fields and multiple properties which refer to the appropriate bits within those bytes.您将不得不做一些更复杂的事情,例如使用两个私有字节字段和多个属性,这些属性引用这些字节中的适当位。

Here is a implementation of that, each item you increment the right hand side of the 1 << _ to move to the next bit field.这是它的一个实现,您增加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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM