C#-编写处理位操作的通用方法

C#-编写处理位操作的通用方法,c#,generics,bit-manipulation,C#,Generics,Bit Manipulation,我正在开发一个工具,该工具必须符合一个规范,该规范将大量数据打包到跨字节边界的位中。示例:2字节编码2个字段,10位值,6位公差。其他字段可能跨越2-4个字节,并分为多个字段 我认为另一种选择是在发送数据之前/接收数据之后创建通用的位打包/解包函数,并使用标准类型(byte、short、int、long等)处理C#中的所有数据,而不是与C#抗争并尝试使用位字段获取结构 我是C#的新手,所以我不确定最好的方法。据我所知,不鼓励将不安全的与指针一起使用,但我尝试使用泛型类型却惨遭失败: privat

我正在开发一个工具,该工具必须符合一个规范,该规范将大量数据打包到跨字节边界的位中。示例:2字节编码2个字段,10位值,6位公差。其他字段可能跨越2-4个字节,并分为多个字段

我认为另一种选择是在发送数据之前/接收数据之后创建通用的位打包/解包函数,并使用标准类型(byte、short、int、long等)处理C#中的所有数据,而不是与C#抗争并尝试使用位字段获取结构

我是C#的新手,所以我不确定最好的方法。据我所知,不鼓励将
不安全的
与指针一起使用,但我尝试使用泛型类型却惨遭失败:

private static bool GetBitsFromByte<T,U>(T input, byte count, out U output, byte start = 0) where T:struct where U:struct
{
    if (input == default(T))
        return false;

    if( (start + count) > Marshal.SizeOf(input))
        return false;

    if(count > Marshal.SizeOf(output))
        return false;

    // I'd like to setup the correct output container based on the
    // number of bits that are needed
    if(count <= 8)
        output = new byte();
    else if (count <= 16)
        output = new UInt16();
    else if (count <= 32)
        output = new UInt32();
    else if (count <= 64)
        output = new UInt64();
    else
        return false;

    output = 0; // Init output

    // Copy bits out in order
    for (int i = start; i < count; i++)
    {
        output |= (input & (1 << i));  // This is not possible with generic types from my understanding
    }
    return true; 
}
我不想为
字节
ushort
uint
ulong
的所有可能组合编写函数,尽管我想这是一种替代方法

我已经研究了
位转换器
类,但这是针对不处理位的字节数组的。我也明白我不能做类似于:
where T:INumeric
where T:System.ValueType
,所以我愿意接受建议


谢谢

正如您所知,您不能在t:INumeric
中执行
,因此无论您编写什么,都可能需要有一些变体来支持不同的数字类型

如有必要,我可能会使用和write方法与其他数据类型进行转换。然后,您最多需要一个从和到每个数字类型的方法,而不是每个类型组合一个方法。(C#中有,所以最坏的情况是8+8=16,而不是8*8=64)

如果您不喜欢手动复制/粘贴并在发生更改时更新的想法,您可能可以使用生成8-ish整数类型的方法

uint data_in = 0xdeadbeef;
ushort data_out;
byte next_data_out;
// pay attention to BitConverter.IsLittleEndian here!
// you might need to write your own conversion methods,
// or do a Reverse() or find a better library
var bits = new BitArray(BitConverter.GetBytes(data_in));
if (bits.TryConvertToUInt16(out data_out, 10))
{
    Console.WriteLine(data_out.ToString("X")); // 2EF
    if (bits.TryConvertToByte(out next_data_out, 6, 10))
    {
        Console.WriteLine(next_data_out.ToString("X")); // 2F
    }
}


private static bool Validate(BitArray bits, int len, int start, int size)
{
    return len < size * 8 && bits.Count > start + len;
}
public static bool TryConvertToUInt16(this BitArray bits, out ushort output, int len, int start = 0)
{
    output = 0;
    if (!Validate(bits, len, start, sizeof(ushort)))
        return false;
    for (int i = start; i < len + start; i++)
    {
        output |= (ushort)(bits[i] ? 1 << (i - start) : 0);
    }
    return true;
}
public static bool TryConvertToByte(this BitArray bits, out byte output, int len, int start = 0)
{
    output = 0;
    if (!Validate(bits, len, start, sizeof(byte)))
        return false;
    for (int i = start; i < len + start; i++)
    {
        output |= (byte)(bits[i] ? 1 << (i - start) : 0);
    }
    return true;
}
uint data\u in=0xdeadbeef;
输出数据;
字节next\u data\u out;
//注意这里的BitConverter.IsLittleEndian!
//您可能需要编写自己的转换方法,
//或者做一个反向()或者找到一个更好的库
var bits=新的位数组(BitConverter.GetBytes(data_in));
中频(位TryConvertToUInt16(输出数据,10))
{
Console.WriteLine(data_out.ToString(“X”);//2EF
if(位TryConvertToByte(输出下一个数据输出,6,10))
{
Console.WriteLine(next_data_out.ToString(“X”);//2F
}
}
私有静态bool验证(位数组位、int len、int start、int size)
{
返回lenstart+len;
}
公共静态布尔TRYCONVERTTOUNT16(此位数组位,out ushort输出,int len,int start=0)
{
输出=0;
如果(!验证(位、长度、开始、大小(ushort)))
返回false;
对于(int i=start;i输出|=(ushort)(位[i]?1这里发生了两件事:

  • 如果有out参数,则必须在函数中的某个位置为其赋值。类似这样的语句无效:

    if( (start + count) > Marshal.SizeOf(input))
        return false; // no assignment to output!
    
  • 类似地,您有许多输出赋值。不要这样做,您在声明中将输出的类型指定为type
    U

    // don't do this
    if(count <= 8)
         output = new byte();
    if (...) //etc 
    // do this
    output = new U();
    
  • 因此,您可能会使用具有硬编码输出的版本(使U成为硬编码类型),但从我的角度来看,尝试使用
    泛型类型几乎是不可能的


    编辑:想一想,我也不确定你是否能够在泛型结构上执行按位操作。

    如果你想在任何随机结构上实现这一点,这有点像序列化问题。有关这方面的信息,请参阅此线程:

    以下是上述概念,稍作修改,使其具有通用性:

    class GenericSerializer <T>
    {
        public BitArray ToBitArray(T input, int start, int len)
        {
            int structSize = Marshal.SizeOf(input);
            BitArray ret = new BitArray(len);
            int byteStart = start / 8;
            int byteEnd = (start + len) / 8 + 1;
            byte[] buffer = new byte[byteEnd - byteStart];
    
            IntPtr ptr = Marshal.AllocHGlobal(structSize);
            Marshal.StructureToPtr(input, ptr, false);
            Marshal.Copy(ptr, buffer, byteStart, buffer.Length);
            Marshal.FreeHGlobal(ptr);
    
            int destBit = 0;
            int sourceBit = start % 8;
            int sourceEnd = sourceBit + len;
            while (sourceBit < sourceEnd)
            {
                ret[destBit] = 0 != (buffer[sourceBit / 8] 
                    & (1 << (sourceBit % 8)));
                ++sourceBit;
                ++destBit;
            }
    
            return ret;
        }
    
        public T FromBytes(byte[] arr)
        {
            IntPtr ptr = Marshal.AllocHGlobal(arr.Length);
            Marshal.Copy(arr, 0, ptr, arr.Length);
    
            T output = (T)Marshal.PtrToStructure(ptr, typeof(T));
            Marshal.FreeHGlobal(ptr);
    
            return output;
        }
    }
    
    类泛型序列化程序
    {
    公共位数组ToBitArray(T输入、int开始、int len)
    {
    int structSize=Marshal.SizeOf(输入);
    BitArray ret=新的BitArray(len);
    int byteStart=start/8;
    int byteEnd=(开始+长度)/8+1;
    byte[]buffer=新字节[byteEnd-byteStart];
    IntPtr ptr=Marshal.AllocHGlobal(structSize);
    Marshal.StructureToPtr(输入,ptr,false);
    Marshal.Copy(ptr、buffer、byteStart、buffer.Length);
    弗里赫全球元帅(ptr);
    int-destBit=0;
    int sourceBit=start%8;
    int sourceEnd=sourceBit+len;
    while(sourceBit& (1)你认为调用非托管C/C++代码吗?它可能更容易。@杰伊我希望在C中做这一切,但是如果所有其他的都失败了,那就是回落计划。@ NeuliStOne谢谢,首先更新为什么它需要是通用的?为什么不分开方法或方法重载?这好像你可以在64位中做所有的计算,然后向下CON。将vert存储回结构中。甚至可能运行得更快…(或者T是否大于64位?)我很困惑
    相关的\u位是否会持有
    bool
    IEnumerable
    ?如何有效地将其转换为
    ushort
    ?您可能会使用一个循环,与问题代码中的
    类似。我更愿意使用
    字节[]
    位转换器
    。感觉速度会快得多…@ebyrob使用
    字节[]可能快得多
    ,但这可能不重要。
    位数组
    在内部是一个
    int
    数组,我认为
    位转换器
    不会处理他需要的索引。如果你喜欢使用
    字节[]
    而不是
    位数组
    你可以这样做。我是在与
    IEnumerable
    进行比较。是的
    位数组
    如果使用正确,速度可能相当快。
    // impossible to infer from a parameter constraint of "struct" 
    output = 0; // Init output
    
    class GenericSerializer <T>
    {
        public BitArray ToBitArray(T input, int start, int len)
        {
            int structSize = Marshal.SizeOf(input);
            BitArray ret = new BitArray(len);
            int byteStart = start / 8;
            int byteEnd = (start + len) / 8 + 1;
            byte[] buffer = new byte[byteEnd - byteStart];
    
            IntPtr ptr = Marshal.AllocHGlobal(structSize);
            Marshal.StructureToPtr(input, ptr, false);
            Marshal.Copy(ptr, buffer, byteStart, buffer.Length);
            Marshal.FreeHGlobal(ptr);
    
            int destBit = 0;
            int sourceBit = start % 8;
            int sourceEnd = sourceBit + len;
            while (sourceBit < sourceEnd)
            {
                ret[destBit] = 0 != (buffer[sourceBit / 8] 
                    & (1 << (sourceBit % 8)));
                ++sourceBit;
                ++destBit;
            }
    
            return ret;
        }
    
        public T FromBytes(byte[] arr)
        {
            IntPtr ptr = Marshal.AllocHGlobal(arr.Length);
            Marshal.Copy(arr, 0, ptr, arr.Length);
    
            T output = (T)Marshal.PtrToStructure(ptr, typeof(T));
            Marshal.FreeHGlobal(ptr);
    
            return output;
        }
    }