我正在开发一种工具,必须符合规范,该规范在字节边界内有大量的打包数据。示例:2个字节编码2个字段,10位值,6位容差。其他字段可能跨越2-4个字节并分成更多字段。
,而不是与C#打并尝试获取的Structs与位字段(如在C ++),我想另一种选择是创建通用位打包/右使用C#发送/接收数据后,并处理所有的数据之前拆包功能标准类型:byte,short,int,long等。
我是C#的新手,所以我不确定接近这个的最好方法。根据我的阅读,不鼓励使用unsafe
和指针,但我尝试使用泛型类型失败了:
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;
}
我将用这样的方法调用方法将10位(从LSB)拉出data_in
到data_out
,将接下来的6位从data_in
拉到{{1} }}
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
的所有可能组合编写函数,但我想这是另一种选择。< / p>
我已经查看了ulong
类,但这是用于不操作位的字节数组。我也理解我不能执行类似:BitConverter
或where T : INumeric
的操作,因此我愿意接受建议。
谢谢!
答案 0 :(得分:3)
如你所知,你不能where T : INumeric
,所以无论你写什么,都可能需要有一些变化来支持不同的数字类型。
我可能会使用BitArray
并根据需要编写方法来转换为/从其他数据类型转换。然后,您最多需要一种方法来自和来自每种数字类型,而不是每种类型组合的方法。 (在C#中有8-ish integer types,所以最坏的情况是大约8 + 8 = 16,而不是8 * 8 = 64)
如果您不喜欢手动复制/粘贴的想法,并且在某些内容发生变化时更新,则可以使用T4 Templates生成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;
}
答案 1 :(得分:1)
这里有几件事情发生了:
当你有一个out参数时,你必须在你的函数中的某个地方分配它。这样的陈述无效:
if( (start + count) > Marshal.SizeOf(input))
return false; // no assignment to output!
同样,您有很多输出分配。不要这样做,您在声明中将输出的类型指定为类型U
// don't do this
if(count <= 8)
output = new byte();
if (...) //etc
// do this
output = new U();
即使纠正这两个,我仍然不确定你能得到多远。您将无法从泛型类型中推断出任何操作,我认为您无法为它们分配值。
// impossible to infer from a parameter constraint of "struct"
output = 0; // Init output
因此,您可能会使用具有硬编码输出的版本(使U成为硬编码类型),但从我的角度来看,尝试拥有out
泛型类似乎几乎是不可能的。
编辑:想一想,我不确定你是否能够在通用结构上执行按位操作。
答案 2 :(得分:0)
如果你想在任何随机结构上使用它,那就会产生序列化问题。有关该信息的一些信息,请参阅此主题:
How to convert a structure to a byte array in C#?
以上是上述概念,通过一些修改使其具有通用性:
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等)可能会运行得更快。