Маршалиране на елементи с битови полета в C#

Възможно ли е да маршалирате структура в стил 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;
}

person Jo-Herman Haugholt    schedule 16.02.2012    source източник
comment
Разгледайте това: stackoverflow.com/questions/14464/bit-fields-in-c- остър   -  person Tony The Lion    schedule 16.02.2012


Отговори (4)


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

Избягвам да добавям сетери, защото харесвам неизменни структури, но по принцип можете да добавите и тях.

person CodesInChaos    schedule 16.02.2012
comment
Приблизително така, както съм го правил досега. Надявах се да има нещо, което съм пропуснал, но предполагам, че c'est la vie. - person Jo-Herman Haugholt; 16.02.2012

Това е моят "безопасен c#" порт на rgb16 структура.

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

}
person Ian    schedule 18.02.2014

Прекарах по-голямата част от вчера, опитвайки се да разреша този проблем като по-голяма част от въпроса за „Представяне на обединени битови полета с помощта на StrucLayout и FieldOffset на c#“, който не само ще отговори на въпроса ви (по-горе), но може да бъде намерен тук:

Представяне на обединени битови полета с помощта на StrucLayout и FieldOffset на c#

По същество ще трябва да дефинирате структура (тип стойност) и да използвате обекта BitVector32, за да дефинирате секцията за битово поле за всяко битово поле, което искате да представите. Можете да пропуснете частта за съюза, тъй като това не се отнася до вашия въпрос, но по-голямата част от публикацията все още се отнася до вашия въпрос.

Просто за забавление, реших да създам C# структурата за вашия RGB16 пример:

Забележка: Обектът 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. Уверете се, че спазвате „endianness“ на целочислените си типове данни
  4. Избягвайте, ако изобщо е възможно, каквито и да било връзки с основния език, т.е. избягвайте мениджъра на езиковата памет - придържайте се към "обикновени стари типове данни". Това ще направи маршалинга на данни по кабела много по-прост.
person Stacy Dudovitz    schedule 24.11.2018

Маршалирах битови полета като:

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, ако искате (не е много компактен)

Upd: Връзката, предоставена от Тони в коментарите към вашия въпрос, използва същата идея, но изглежда по-точна от моята, така че използвайте неговото решение, ако не можете да намерите по-добро

person Archeg    schedule 16.02.2012