在C#中使用位字段编组结构

时间:2012-02-16 12:44:24

标签: c# marshalling bit-fields

是否可以将包含位字段的C风格结构编组到C#结构中,或者您是否必须将其编组为基本类型然后执行位掩码?

E.g。我想从这样的C风格结构编组:

struct rgb16 {
    unsigned int R : 4;
    unsigned int G : 5;
    unsigned int B : 4;
}

并将其编组为类似的东西:

[StructLayout(LayoutKind.Sequential)]
public struct Rgb16 {
    public byte R;
    public byte G;
    public byte B;
}

4 个答案:

答案 0 :(得分:6)

C#中没有位字段。因此,我将使用封装了小提琴的属性:

[StructLayout(LayoutKind.Sequential)]
public struct Rgb16 {
    private readonly UInt16 raw;
    public byte R{get{return (byte)((raw>>0)&0x1F);}}
    public byte G{get{return (byte)((raw>>5)&0x3F);}}
    public byte B{get{return (byte)((raw>>11)&0x1F);}}

    public Rgb16(byte r, byte g, byte b)
    {
      Contract.Requires(r<0x20);
      Contract.Requires(g<0x40);
      Contract.Requires(b<0x20);
      raw=r|g<<5|b<<11;
    }
}

我避免添加setter,因为我喜欢不可变的结构,但原则上你也可以添加它们。

答案 1 :(得分:3)

这是我的rgb16结构的“安全c#”端口。

[StructLayout(LayoutKind.Explicit, Size = 2, Pack = 1)]
public class Color16
{
    // Btifield: 5
    [FieldOffset(0)]
    private ushort b_i;

    public ushort b
    {
        get { return (ushort)((b_i >> 11) & 0x1F); }
        set { b_i = (ushort)((b_i & ~(0x1F << 11)) | (value & 0x3F) << 11); }
    }

    // Bitfield: 6
    [FieldOffset(0)]
    private ushort g_i;

    public ushort g
    {
        get { return (ushort)((g_i >> 5) & 0x7F); }
        set { g_i = (ushort)((g_i & ~(0x7F << 5)) | (value & 0x7F) << 5); }
    }

    // Bitfield: 5
    [FieldOffset(0)]
    private ushort r_i;

    public ushort r
    {
        get { return (ushort) (r_i & 0x1F); }
        set { r_i = (ushort) ((r_i & ~0x1F) | (value & 0x1F)); }
    }

    [FieldOffset(0)]
    public ushort u;

    public Color16() { }
    public Color16(Color16 c) { u = c.u; }
    public Color16(ushort U) { u = U; }

}

答案 2 :(得分:0)

我已经整理了像:

这样的位域
public struct Rgb16 {
    public ushort Value; // two byte value that internally contain structure R(4):G(5):B(4)

    public Rgb16BitField GetBitField
    {
        get; set;
    }
}

其中属性通过将Value划分为位来创建新结构。

这不是最好的方法,但没有发现任何其他对我有用的方法。 如果你愿意,我可以为GetBitField提供代码(它不是很紧凑)

更新:Tony在您的问题评论中提供的链接使用相同的想法,但似乎比我的更准确,所以如果找不到更好的话,请使用他的解决方案

答案 3 :(得分:0)

昨天我花了大部分时间来尝试解决此问题,这是“使用c#的StrucLayout和FieldOffset表示联合位域”问题的较大部分,该问题不仅可以回答您上述问题,而且可以在此处找到:

Representing union bitfields using c#'s StrucLayout and FieldOffset

本质上,您将需要定义一个结构(值类型),并使用 BitVector32 对象为要表示的每个位域定义位域部分。您可以跳过有关工会的部分,因为这与您的问题无关,但是大部分帖子仍与您的问题有关。

只是出于乐趣,我想我会为您的RGB16示例添加C#结构:

注意:BitVector32对象的长度为32位,因此绰号上的“ 16”有点误导性……请注意这一点

[StructLayout(LayoutKind.Explicit, Size = 1, CharSet = CharSet.Ansi)]
public struct Rgb16
{
    #region Lifetime

    /// <summary>
    /// Ctor
    /// </summary>
    /// <param name="foo"></param>
    public Rgb16(int foo)
    {
        // allocate the bitfield
        buffer = new BitVector32(0);

        // initialize bitfield sections
        r = BitVector32.CreateSection(0x0f);        // 4
        g = BitVector32.CreateSection(0x1f, r);     // 5
        b = BitVector32.CreateSection(0x0f, g);     // 4
    }

    #endregion

    #region Bifield

    // Creates and initializes a BitVector32.
    [FieldOffset(0)]
    private BitVector32 buffer;

    #endregion

    #region Bitfield sections

    /// <summary>
    /// Section - Red
    /// </summary>
    private static BitVector32.Section r;

    /// <summary>
    /// Section - Green
    /// </summary>
    private static BitVector32.Section g;

    /// <summary>
    /// Section - Blue
    /// </summary>
    private static BitVector32.Section b;

    #endregion

    #region Properties

    /// <summary>
    /// Flag 1
    /// </summary>
    public byte R
    {
        get { return (byte)buffer[r]; }
        set { buffer[r] = value; }
    }

    /// <summary>
    /// Flag 2
    /// </summary>
    public byte G
    {
        get { return (byte)buffer[g]; }
        set { buffer[g] = value; }
    }

    /// <summary>
    /// Flag 1
    /// </summary>
    public byte B
    {
        get { return (byte)buffer[b]; }
        set { buffer[b] = value; }
    }

    #endregion

    #region ToString

    /// <summary>
    /// Allows us to represent this in human readable form
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return $"Name: {nameof(Rgb16)}{Environment.NewLine}Red: {R}: Green: {G} Blue: {B}  {Environment.NewLine}BitVector32: {buffer}{Environment.NewLine}";
    }

    #endregion
}

要使用此功能,您需要进行如下分配:

internal static class Program
{
    /// <summary>
    /// Main entry point
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {

        var rgb16 = new Rgb16(0)
        {
            R = 24,
            G = 16,
            B = 42
        };

另外,请注意,对此有一个引用:

Bit fields in C#

这里还有许多其他答案,但是它们有很多陷阱要注意。也许我在这里能做的最好的事情就是列出您可能想要的东西:

  1. 确保将数据打包在字节边界上
  2. 请确保指定数据类型的大小,即 int 会根据硬件更改大小,而 System.Int32 不会。
  3. 确保您尊重整数数据类型的“字节序”
  4. 尽可能避免与基础语言有任何联系,即避免语言存储管理器-坚持“普通的旧数据类型”。这样可以简化整个网络中的封送数据。