来自特定索引的字节数组作为 c# 中的结构而不进行复制
byte array from specific index as struct in c# without making a copy
目前,我编写客户端-服务器垃圾代码并处理大量通过网络传递的 C++ 结构。
我知道这里提供的方法 Reading a C/C++ data structure in C# from a byte array,但它们都是关于制作副本的。
我想要这样的东西:
struct/*or class*/ SomeStruct
{
public uint F1;
public uint F2;
public uint F3;
}
稍后在我的代码中我想要这样的东西:
byte[] Data; //16 bytes that I got from network
SomeStruct PartOfDataAsSomeStruct { get { return /*make SomeStruct instance based on this.Data starting from index 4, without copying it. So when I do PartOfDataAsSomeStruct.F1 = 132465; it also changes bytes 4, 5, 6 and 7 in this.Data.*/; } }
如果可以,请告诉我怎么做?
像这样?
byte[] data = new byte[16];
// 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
Console.WriteLine(BitConverter.ToString(data));
ref SomeStruct typed = ref Unsafe.As<byte, SomeStruct>(ref data[4]);
typed.F1 = 42;
typed.F2 = 3;
typed.F3 = 9;
// 00-00-00-00-2A-00-00-00-03-00-00-00-09-00-00-00
Console.WriteLine(BitConverter.ToString(data));
这使用作为数据的“内部管理指针”的 ref-local 从字节数组的中间强制数据。零拷贝。
如果您需要多个项目(例如向量的工作方式),您可以使用 span 和 MemoryMarshal.Cast
做同样的事情
请注意,它对元素使用 CPU-endian 规则 - 在我的例子中是小端。
对于跨度:
byte[] data = new byte[256];
// create a span of some of it
var span = new Span<byte>(data, 4, 128);
// now coerce the span
var typed = MemoryMarshal.Cast<byte, SomeStruct>(span);
Console.WriteLine(typed.Length); // 10 of them fit
typed[3].F1 = 3; // etc
感谢您的更正,Marc Gravell。谢谢你的例子。
这里有一种使用 Class 和按位运算符的方法,不用指针,来做同样的事情:
class SomeClass
{
public byte[] Data;
public SomeClass()
{
Data = new byte[16];
}
public uint F1
{
get
{
uint ret = (uint)(Data[4] << 24 | Data[5] << 16 | Data[6] << 8 | Data[7]);
return ret;
}
set
{
Data[4] = (byte)(value >> 24);
Data[5] = (byte)(value >> 16);
Data[6] = (byte)(value >> 8);
Data[7] = (byte)value;
}
}
}
测试:
SomeClass sc = new SomeClass();
sc.F1 = 0b_00000001_00000010_00000011_00000100;
Console.WriteLine(sc.Data[3].ToString() + " " + sc.Data[4].ToString() + " " + sc.Data[5].ToString() + " " + sc.Data[6].ToString());
Console.WriteLine(sc.F1.ToString());
//Output:
//1 2 3 4
//16909060
目前,我编写客户端-服务器垃圾代码并处理大量通过网络传递的 C++ 结构。 我知道这里提供的方法 Reading a C/C++ data structure in C# from a byte array,但它们都是关于制作副本的。
我想要这样的东西:
struct/*or class*/ SomeStruct
{
public uint F1;
public uint F2;
public uint F3;
}
稍后在我的代码中我想要这样的东西:
byte[] Data; //16 bytes that I got from network
SomeStruct PartOfDataAsSomeStruct { get { return /*make SomeStruct instance based on this.Data starting from index 4, without copying it. So when I do PartOfDataAsSomeStruct.F1 = 132465; it also changes bytes 4, 5, 6 and 7 in this.Data.*/; } }
如果可以,请告诉我怎么做?
像这样?
byte[] data = new byte[16];
// 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
Console.WriteLine(BitConverter.ToString(data));
ref SomeStruct typed = ref Unsafe.As<byte, SomeStruct>(ref data[4]);
typed.F1 = 42;
typed.F2 = 3;
typed.F3 = 9;
// 00-00-00-00-2A-00-00-00-03-00-00-00-09-00-00-00
Console.WriteLine(BitConverter.ToString(data));
这使用作为数据的“内部管理指针”的 ref-local 从字节数组的中间强制数据。零拷贝。
如果您需要多个项目(例如向量的工作方式),您可以使用 span 和 MemoryMarshal.Cast
请注意,它对元素使用 CPU-endian 规则 - 在我的例子中是小端。
对于跨度:
byte[] data = new byte[256];
// create a span of some of it
var span = new Span<byte>(data, 4, 128);
// now coerce the span
var typed = MemoryMarshal.Cast<byte, SomeStruct>(span);
Console.WriteLine(typed.Length); // 10 of them fit
typed[3].F1 = 3; // etc
感谢您的更正,Marc Gravell。谢谢你的例子。
这里有一种使用 Class 和按位运算符的方法,不用指针,来做同样的事情:
class SomeClass
{
public byte[] Data;
public SomeClass()
{
Data = new byte[16];
}
public uint F1
{
get
{
uint ret = (uint)(Data[4] << 24 | Data[5] << 16 | Data[6] << 8 | Data[7]);
return ret;
}
set
{
Data[4] = (byte)(value >> 24);
Data[5] = (byte)(value >> 16);
Data[6] = (byte)(value >> 8);
Data[7] = (byte)value;
}
}
}
测试:
SomeClass sc = new SomeClass();
sc.F1 = 0b_00000001_00000010_00000011_00000100;
Console.WriteLine(sc.Data[3].ToString() + " " + sc.Data[4].ToString() + " " + sc.Data[5].ToString() + " " + sc.Data[6].ToString());
Console.WriteLine(sc.F1.ToString());
//Output:
//1 2 3 4
//16909060