编程语言
首页 > 编程语言> > C#-编写通用方法来处理位操作

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

作者:互联网

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

我发现不是替代C#并尝试获取具有Bitfields的Structs(如C一样),而是想到了另一种选择,即在发送/接收数据之前创建通用的位打包/拆包功能,并使用标准类型处理C#中的所有数据:byte,short,int,long等.

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

我会用类似这样的方法来调用该方法,以便从data_in中将10位(从LSB)拉出到data_out中,并将接下来的6位从data_in中拉出到next_data_out中.

Uint32 data_in = 0xdeadbeef;
Uint16 data_out;
byte next_data_out;
if(GetBitsFromByte<Uint32,Uint16>(data_in, 10, out data_out, 0))
{
    // data_out should now = 0x2EF
    if(GetBitsFromByte<Uint32,byte>(data_in, 6, out next_data_out, data_out.Length))
    {
        // next_data_out should now = 0x2F
    }
}

我宁愿不必为byte,ushort,uint,ulong的所有可能组合编写函数,尽管我想这是另一种选择.

我已经看过BitConverter类,但这是针对不处理位的字节数组.我也知道我不能做类似的事情:T:INumeric或T:System.ValueType,所以我愿意接受建议.

谢谢!

解决方法:

如您所知,您无法在T:INumeric位置进行操作,因此无论您编写什么内容,都可能必须具有一些变体才能支持不同的数字类型.

我可能会使用BitArray并根据需要编写方法来与其他数据类型进行相互转换.然后,您最多将需要一种从数字类型到数字类型的方法,而对于每种类型的组合则不需要一种. (C#中有8-ish integer types,因此最坏的情况是8 8 = 16,而不是8 * 8 = 64)

如果您不喜欢手动复制/粘贴的想法,并且在某些情况发生变化时对其进行更新,则可以使用T4 Templates为8位整数类型生成方法.

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

标签:bit-manipulation,generics,c
来源: https://codeday.me/bug/20191030/1966356.html