将字节数组转换为对象

3

在C#中有没有一种方法将普通的字节数组转换为对象?

例如,给定以下类:

class Data
{
    public int _int1;
    public int _int2;
    public short _short1;
    public long _long1;
}

我希望基本上能够做到这样:

var bytes = new byte[] { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0 };
var obj = (Data)bytes;

你想要实现什么目标?在你的字节流和你希望创建的实例之间必须有一些登录关系。 - Björn Boxstart
2
从技术上讲,你可以实现一个操作符,即public static explicit operator Data(Byte[] source) {...},但序列化是更好的解决方案。 - Dmitry Bychenko
1
获取一些思路 - 请点击此处 - Pedram
看起来你正在尝试读取一些从C/C++结构体中写入的数据。是这种情况吗? - Matthew Watson
感谢您的评论,我知道我可以使用BitConverter将每个成员单独转换,但我无法反序列化,因为数据一开始就没有被序列化,它只是原始数据。在C++中可以完成这项工作,只需说出我知道这个数据块实际上是这个结构,将其视为此结构,但在C#中可能无法轻松完成此操作。 - Mark
4个回答

8
你可以尝试使用序列化(marshalling):
将你的类声明为Sequential类型(请注意,你需要使用Pack = 1):
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class Data
{
    public int _int1;
    public int _int2;
    public short _short1;
    public long _long1;
}

将字节序列组合成 Data 类的一个新实例:
var bytes = new byte[] { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0 };
GCHandle gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var data = (Data)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(Data));
gcHandle.Free();

// Now data should contain the correct values.

Console.WriteLine(data._int1);    // Prints 1
Console.WriteLine(data._int2);    // Prints 2
Console.WriteLine(data._short1);  // Prints 3
Console.WriteLine(data._long1);   // Prints 4

为了方便,您可以在Data类中编写一个静态方法来进行转换:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
class Data
{
    public int _int1;
    public int _int2;
    public short _short1;
    public long _long1;

    public static Data FromBytes(byte[] bytes)
    {
        GCHandle gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        var data = (Data)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(Data));
        gcHandle.Free();
        return data;
    }
}

...

var data = Data.FromBytes(new byte[] {1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0});

如果你真的想要,你可以编写一个显式运算符来将字节数组转换为所需语法。不过我建议使用 Data.FromBytes(),在我看来这会更加清晰易懂。

不过,为了完整起见:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
class Data
{
    public int _int1;
    public int _int2;
    public short _short1;
    public long _long1;

    public static explicit operator Data(byte[] bytes)
    { 
        GCHandle gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        var data = (Data)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(Data));
        gcHandle.Free();
        return data;
    }
}

...

var data = (Data)new byte[] {1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0};

谢谢,我认为考虑到我想要做的事情,这是最好的答案。 - Mark
大端系统怎么办? - Geograph
@Geograph 你必须确保字节的顺序是正确的。(OP想要与C++转换相同的效果。) - Matthew Watson

2
使用BitConverter.ToInt32/Int16/Int64方法。您只需要指定起始索引即可,例如:
Data data = new Data();
data._int1 = BitConverter.ToInt32(bytes, 0);
data._int2 = BitConverter.ToInt32(bytes, 4);
data._short1 = BitConverter.ToInt16(bytes, 8);
data._long1 = BitConverter.ToInt64(bytes,10);

请记住:

BitConverter.ToInt32

字节数组中的字节顺序必须反映计算机系统体系结构的字节序。


1
这里有一种将字节数组转换为对象的方法。
var binaryFormatter = new BinaryFormatter();
using (var ms = new MemoryStream(bytes))
{
    object obj = binaryFormatter.Deserialize(ms);
    return (Data)obj;
}

0

没有一种方法可以一次性完成转换。

但是你可以在BitConverter的基础上构建:

var d = new Data();
var sI32 = sizeof(Int32);
d._int1 = BitConverter.ToInt32(bytes, 0);
d._int2 = BitConverter.ToInt32(bytes, sI32);
d._short1 = BitConverter.ToInt16(bytes, 2*sI32);
…

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接