C#中实现位域功能

此需求多用于使用C#调用C语言的库,由于C语言写的程序为了节省内存,通常会使用到位域这种数据结构,而C#中没有对应的位域结构,因此需要用其他的语法模拟出位域的效果。

位域

位段(或称“位域”,Bit field)为一种数据结构,可以把数据以的形式紧凑的储存,并允许程序员对此结构的位进行操作。相邻的两个位域如果基类型(underlying type)的长度相同,在后的位域适合当前内存分配单元且没有跨内存分配边界,那么这两个位域分配到同一个(1、2或4字节的)分配单元。可以通俗理解为:具有相同的基类型(underlying type)长度的相邻位域尽量装入基类型的同一个对象,如果装得下的话。

上面是一个C语言的头文件声明,此次的需要就是要用C#去建立一个结构体能与C的结构体对应上。我们先看实现。

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public class Frame
        {
        
            public UInt64 Timestamp;     // us

            public UInt32 Id;


            private UInt16 Flags;//Flags用于存放下列值,并用C#属性来实现位域的功能。

            //属性用于访问位域
            public UInt16 SendType  // 仅发送有效, 接收为0, 0:正常发送, 1:单次发送 2:自发自收
            {
                get { return (ushort)(Flags & (0x0003)); }
                set { Flags = (ushort)((Flags & ~0x0003) | (value & 0x0003)); }
            }
            public UInt16 Tx           // 1:tx 2:rx
            {
                get { return (ushort)((Flags & (0x0004)) >> 2); }
                set { Flags = (ushort)((Flags & ~0x0004) | ((value << 2) & 0x0004)); }
            }
            public UInt16 Echo
            {
                get { return (ushort)((Flags & (0x0008)) >> 3); }
                set { Flags = (ushort)((Flags & ~0x0008) | ((value << 3) & 0x0008)); }
            }
            public UInt16 Fd           // 1:canfd 2:can
            {
                get { return (ushort)((Flags & (0x0010)) >> 4); }
                set { Flags = (ushort)((Flags & ~0x0010) | ((value << 4) & 0x0010)); }
            }
            public UInt16 Rtr          // 1:remote 0:data frame
            {
                get { return (ushort)((Flags & (0x0020)) >> 5); }
                set { Flags = (ushort)((Flags & ~0x0020) | ((value << 5) & 0x0020)); }
            }
            public UInt16 Ext         // 1:extend 0:standard
            {
                get { return (ushort)((Flags & (0x0040)) >> 6); }
                set { Flags = (ushort)((Flags & ~0x0040) | ((value << 6) & 0x0064)); }
            }
            public UInt16 Err          // 1:error frame 0:normal frame;
            {
                get { return (ushort)((Flags & (0x080)) >> 7); }
                set { Flags = (ushort)((Flags & ~0x0009) | ((value << 7) & 0x0009)); }
            }
            public UInt16 Brs          // 1:canfd加速 0:不加速
            {
                get { return (ushort)((Flags & (0x0100)) >> 8); }
                set { Flags = (ushort)((Flags & ~0x000A) | ((value << 8) & 0x000A)); }
            }
            public UInt16 Esi          // 1:被动错误 0:主动错误
            {
                get { return (ushort)((Flags & (0x0200)) >> 9); }
                set { Flags = (ushort)((Flags & ~0x000B) | ((value << 9) & 0x000B)); }
            }
            public UInt16 Reserved     // 保留
            {
                get { return (ushort)((Flags & (0x7C00)) >> 10); }
                set { Flags = (ushort)((Flags & ~0x7C00) | ((value << 10) & 0x7C00)); }
            }
            public UInt16 Trigger      // 触发位
            {
                get { return (ushort)((Flags & (0x8000)) >> 15); }
                set { Flags = (ushort)((Flags & ~0x8000) | ((value << 15) & 0x8000)); }
            }


            public byte Channel;        // 通道号, 若为-1, 代表该报文是发送给所有的通道



            public byte Len;

 
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
            public byte[] Data;

          


        }

属性

属性(Property) 是类,结构,接口的成员。提供一个操作其他私有(private)字段的渠道,为了防止用户篡改结构,类中的字段。用于可以自定义get(),set()访问器中操作字段的方法。

此处使用了C#的属性语法,新建一个Flags的16位整型变量用于存放需要使用位域操作的数据,然后新建了不同的属性语法中来访问Flags中对应的各自数据。此处get()访问器中使用了&的位运算符,以求取得各自的有效数据。

以SendType为例,

占16位中的2个位,并且处于最低的两个位,因此有效位为二进制表达为b0000000000000011转成16进制为0x0003.因此用flags按位与0x0003.便能获取flags中最低两个位的数据,然后再用(ushort)强转成16位整型。


在属性中,当我们需要改变类/结构中某个私有字段时,使用set访问器。

(Flags & ~0x0003):首先用Flags与上求反的0x0003,目的是储存数据的无关部分。
(value & 0x0003):用设定值value与上0x0003,得到数据有效部分.
然后用按位或(|),把两个部分的数据叠加起来。


到这里,我们便完成了对其中某一个数据存与取,然后以此类推,就能够完成整个16位整型中所有数据的存取操作。

相关推荐

  1. C语言13

    2024-01-09 14:46:01       25 阅读
  2. c语言里的

    2024-01-09 14:46:01       27 阅读
  3. C++ 获取成员的

    2024-01-09 14:46:01       55 阅读
  4. C++的作用

    2024-01-09 14:46:01       58 阅读
  5. C++运算符

    2024-01-09 14:46:01       32 阅读
  6. 结构体

    2024-01-09 14:46:01       60 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-09 14:46:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-09 14:46:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-01-09 14:46:01       82 阅读
  4. Python语言-面向对象

    2024-01-09 14:46:01       91 阅读

热门阅读

  1. 第五讲_css元素显示模式

    2024-01-09 14:46:01       60 阅读
  2. 2024.1.5力扣每日一题——队列中可以看到的人数

    2024-01-09 14:46:01       58 阅读
  3. 基于51单片机的智能水表电路设计

    2024-01-09 14:46:01       53 阅读
  4. Android 8.1 默认赋予应用权限

    2024-01-09 14:46:01       62 阅读
  5. Android将自定义View保存为Bitmap图片

    2024-01-09 14:46:01       50 阅读