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

发布于 2025-01-06 04:35:46 字数 445 浏览 4 评论 0原文

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

例如,我想从这样的 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;
}

Is it possible to marshal a C-style struct containing bit-fields to a C# struct, or would you have to marshal it to a basic type and then do bit-masks?

E.g. I would like to marshal from a C style structure like this:

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

And marshal it onto something like this:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

卸妝后依然美 2025-01-13 04:35:46

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

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

There are no bit-fields in C#. So I'd go with properties that encapsulate the bit fiddling:

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

I've avoided adding setters, because I like immutable structs, but in principle you can add them too.

煮茶煮酒煮时光 2025-01-13 04:35:46

这是我的 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; }

}

Thats my "safe c#" port of a rgb16 struct.

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

}
带刺的爱情 2025-01-13 04:35:46

我昨天花了大部分时间试图解决这个问题,作为“使用 c# 的 StrucLayout 和 FieldOffset 表示联合位域”问题的一个更大的部分,它不仅会回答您的问题(上面),而且可以在这里找到:

使用 C# 的 StrucLayout 和 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
        };

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

C# 中的位字段

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

  1. 确保将数据打包在字节边界上
  2. 确保指定数据类型的大小,即 int 更改大小根据硬件的不同,System.Int32 不会。
  3. 确保尊重整数数据类型的“字节顺序”。
  4. 尽可能避免与底层语言的任何联系,即避免语言内存管理器——坚持“普通旧数据类型”。这将使通过网络编组数据变得更加简单。

I spent the better part of yesterday trying to solve this problem as a bigger part of the issue of "Representing union bitfields using c#'s StrucLayout and FieldOffset", which will not only answer you question (above), but can be found here:

Representing union bitfields using c#'s StrucLayout and FieldOffset

Essentially, you will need to define a struct (a value type) and use the BitVector32 object to define the bitfield section for each bitfield you wish to represent. You can skip the part about the union since that does not pertain to your question, but the large majority of the post still pertains to your question.

Just for the fun of it, I thought I would whip up the C# struct for your RGB16 example:

Note: BitVector32 object are 32 bits in length, so the '16' on the moniker is kind of misleading... please take note of that

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

To use this, you would allocate as follows:

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

Also, please note that there is a reference to this:

Bit fields in C#

There are many other answers here, but they have many pitfalls to be aware of. Perhaps the best thing I can do here is just list what you might want to look for:

  1. Be sure to pack your data on a byte boundary
  2. Make sure to specify the size of your data types i.e. int changes size depending on the hardware, System.Int32 does not.
  3. Make sure you honor the 'endianness' of your integer data types
  4. Avoid if at all possible any ties to the underlying language i.e. avoid the language memory manager -- stick to "plain old data types". That will make marshaling data across the wire much simpler.
情魔剑神 2025-01-13 04:35:46

我已经整理了如下位域:

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

    public Rgb16BitField GetBitField
    {
        get; set;
    }
}

其中属性通过将值除以位来创建新的结构,就像您提到的那样。

这不是最好的方法,但还没有找到其他对我有用的方法。
如果您愿意,我可以提供 GetBitField 的代码(它不是很紧凑)

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

I've marshaled bitfields like:

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

    public Rgb16BitField GetBitField
    {
        get; set;
    }
}

where the property creates new structure like you mentioned by dividing Value to bits.

Aren't the best way to do that, but haven't found anything else that worked for me.
I can provide the code for GetBitField if you want (it isn't very compact)

Upd: The link provided by Tony in the comments to your question uses the same idea but seems more accurate then mine, so use his solution if you cannot find any better

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文