没有结构的对象的序列化

Serialization of object without structure

我正在开发一个可以与西门子 PLC(工业控制器)交换数据的软件。 为了让它工作,我需要能够序列化和反序列化一个只包含变量当前值的字节数组。 我面临的问题是 Serialize/Deserialize 方法添加了很多超出变量当前值的信息。 下面的实例class:

    [Serializable]
    public class VarMap
    {
            public byte var1;
            public int64 var2;
            public int32 var3;
    }

序列化后,需要是一个字节数组,包含一个接一个的值,每个值以字节为单位占用它们的大小: [var1 字节 1][var2 字节 1][var2 字节 2][var2 字节 3][var2 字节 4][var3 字节 1][var3 字节 2].

有什么想法可以根据 class 的声明以动态方式实现这一点吗?

对于引用其他引用类型的复杂引用类型,没有一种简单的方法可以将它们扁平化为纯字节序列,并且序列化程序必须使用更复杂的格式来处理此类情况是有原因的。

也就是说,你显示的类型就是所谓的blittable type

Blittable 类型只包含可以直接复制位模式的其他原始类型的字段。

通过将 class 更改为结构,您通常可以将此类类型直接保存到磁盘。这个确实需要用到unsafe block,但是代码比较简单

有很多方法可以在不使用不安全代码的情况下做同样的事情,但使用不安全代码是最多的straight-forward,因为您可以只获取一个指针并使用它直接写入磁盘、网络等。

首先,更改为结构:

[StructLayout(LayoutKind.Sequential)]
public struct VarMap
{
    public byte var1;
    public long var2;
    public int var3;
}

然后,创建一个并直接写入磁盘:

VarMap vm = new VarMap
{
    var1 = 254,
    var2 = 1234,
    var3 = 5678
};

unsafe
{
    VarMap* p = &vm;
    int sz = sizeof(VarMap);
    using FileStream fs = new FileStream(@"out.bin", FileMode.Create);
    ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(p, sz);
    fs.Write(span);
}

但是,如果您在十六进制查看器中查看结果,您会注意到一些事情:

24 个字节被写入磁盘,而不是我们根据您的数据结构中的字段类型假设的 13 个字节。

这是因为为了对齐目的在内存中填充了结构。

根据您将这些字节发送到实际期望的设备(并且 endian-ness 也可能是一个问题),这种技术可能有效也可能无效。

如果您需要绝对控制,您可能希望自己 hand-write 序列化代码,而不是试图找到一些通用机制来执行此操作。使用反射可能是可能的,但直接将您的字段转换为原始字节表示形式可能更简单。