首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# -编写处理位操作的通用方法

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

Stack Overflow用户
提问于 2013-11-04 21:06:17
回答 3查看 865关注 0票数 3

我正在开发一个必须符合规范的工具,该规范将大量数据跨字节边界打包成位。示例:2字节编码2个字段,10位值,6位容忍度。其他字段可能跨2-4字节,并被分解成更多的字段。

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

我对C#并不熟悉,所以我不确定如何最好地解决这个问题。据我所读,不鼓励使用unsafe和指针,但我使用泛型类型的尝试不幸失败了:

代码语言:javascript
复制
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,然后从data_in中提取6位到next_data_out

代码语言:javascript
复制
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
    }
}

我不想为所有可能的byteushortuintulong组合编写函数,尽管我认为这是另一种选择。

我已经看过了BitConverter类,但这是用于字节数组,而不是操作位。我也明白,不能做类似于:where T : INumericwhere T : System.ValueType之类的事情,所以我愿意接受建议。

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-11-04 21:26:07

正如您所知道的,您不能执行where T : INumeric,所以无论您编写什么,都可能需要一些变体来支持不同的数字类型。

我可能会在必要时使用BitArray和write方法来转换/从其他数据类型转换。然后,您最多需要一种方法,用于每种数字类型,而不是每种类型组合的方法。(在8-ish整数类型中有C#,所以最坏的情况是8+8=16,而不是8*8=64)

如果您不喜欢手动复制/粘贴以及在某些事情发生变化时进行更新的想法,您可能可以使用T4模板来生成8 ish整数类型的方法。

代码语言:javascript
复制
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;
}
票数 3
EN

Stack Overflow用户

发布于 2013-11-04 21:34:13

这里发生了几件事:

  1. 当您有out参数时,您必须在函数的某个位置为它赋值。这样的语句无效: 如果((开始+计数)>Marshal.SizeOf(输入))返回false;//没有分配到输出!
  2. 类似地,您有许多用于输出的任务。不要这样做,您在声明中将输出的类型指定为U类型 //不要这样做如果(计数<= 8)输出=新字节();如果(.) //etc /完成此输出=新U();
  3. 即使纠正了这两个问题,我仍然不确定你能走多远。您将无法从泛型类型中推断出任何操作,而且我认为您将无法为它们赋值。 //不可能从"struct“输出=0的参数约束推断;// Init输出

因此,您可以不使用具有硬编码输出(使U成为硬编码类型)的版本,但在我看来,尝试使用out泛型似乎几乎是不可能的。

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

票数 1
EN

Stack Overflow用户

发布于 2013-11-05 16:03:59

如果您想让它在任意随机结构上工作,它有点像序列化问题。有关这方面的一些信息,请参阅这篇文章:

如何在C#中将结构转换为字节数组?

下面是上面的概念,并对其进行了一些修改,使其具有通用性:

代码语言:javascript
复制
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;
    }
}

注意:我只对读做了BitArray,在写时使用了byte []。(这里的一个缺点是,每个操作都要完全复制结构两次,这样就不会很好地执行)

使用BitConverter或从几个已知类型(例如Int32、Int16、Int64等)转到/来自的一组函数可能会运行得更快。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19777116

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档