来自特定索引的字节数组作为 c# 中的结构而无需复制

Posted

技术标签:

【中文标题】来自特定索引的字节数组作为 c# 中的结构而无需复制【英文标题】:byte array from specific index as struct in c# without making a copy 【发布时间】:2020-11-01 05:05:34 【问题描述】:

目前,我编写客户端-服务器垃圾代码,并大量处理通过网络传递的 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.*/;  

如果可以,请告诉我怎么做?

【问题讨论】:

【参考方案1】:

像这样?

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 规则——在我的例子中是 little 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

【讨论】:

@Kosmos 如果您使用的是低级编译器等,您也可以使用unsafe 和非托管指针,但是:上面要容易得多! 哇哦,它的工作!我想避免unsafe 的事情……至少在我遇到没有它就无法解决的问题之前。这次问题解决了。 @Kosmos 请注意,即使您没有使用 unsafe,它仍然是“不安全的”,因为您可能弄错了大小(数组中的字节数不足),这意味着您'正在覆盖别人的记忆。所以只是:不要弄错尺寸:)【参考方案2】:

感谢您的更正,Marc Gravell。谢谢你的例子。

这是一种使用类和位运算符(无需指针)来做同样事情的方法:

    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# 中的结构而无需复制的主要内容,如果未能解决你的问题,请参考以下文章

将文件加载为字节数组,而不在内存中分配它 C#

C# 结构到字节数组封送产生 19 个元素而不是 20 个

无法将固定大小的字节数组从结构复制到 C# 结构中的另一个数组

可以将 Byte[] 数组写入 C# 中的文件吗?

如何使用 C# NAudio 操作字节?

在 Byte[] 数组 c# 中查找第一个特定字节