【问题标题】:Serialization of object without structure无结构对象的序列化
【发布时间】:2020-08-25 21:16:33
【问题描述】:

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

    [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]。

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

【问题讨论】:

标签: c# serialization marshalling


【解决方案1】:

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

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

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

通过将您的类更改为结构,您通常可以将此类类型直接保存到磁盘。这确实需要使用一个不安全的块,但是代码比较简单。

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

首先,改成一个结构体:

[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 个字节。

这是因为结构在内存中被填充以用于对齐目的。

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

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

【讨论】:

  • 由于填充,这将不起作用。正如您所建议的,看来我将不得不编写自己的解决方案。使用 foreach 遍历结构中所有声明的变量,我如何将每个变量转换为各自的字节数组?有没有这样的方法?
  • 查看 BitConverter 和 BinaryPrimitives 类型。前者使用起来稍微简单一些,但代价是总是需要分配内存。如果您不想超优化内存分配,只需在各种原始类型字段上使用 BitConverter.GetBytes()。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-06
  • 1970-01-01
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 2020-08-21
  • 1970-01-01
相关资源
最近更新 更多