6.实现自定义序列化原因

  1. 6.实现自定义序列化原因
    1. 6.1 题目
    2. 6.2 深入解析
    3. 6.3 答题示例
    4. 6.4 关键词联想

6.实现自定义序列化原因


6.1 题目

C#中提供了序列化和反序列化二进制数据的公共类 BinaryFormatter,为什么我们一般还要自己去实现序列化和反序列化二进制数据的方法?


6.2 深入解析

主要原因:

  • 希望数据跨语言:BinaryFormatter是C#的特定格式,不同的语言之间可能无法良好兼容。自定义数据可以让数据跨语言,在网络通讯时,后端的语言大多数情况下不是C#,我们应该和后端一起制定序列化和反序列化的规范。

次要原因:

  1. 更容易进行加密处理。
  2. 节约空间:BinaryFormatter会包含一些除数据以外的额外信息等。

自定义序列化和反序列化方法可以根据需求灵活地定制数据格式,以满足特定的需求和场景。

using System;
using System.IO;

public class PlayerData
{
    public string playerName;
    public int playerScore;

    // 自定义序列化方法
    public byte[] Serialize()
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
            binaryWriter.Write(playerName);
            binaryWriter.Write(playerScore);
            return memoryStream.ToArray();
        }
    }

    // 自定义反序列化方法
    public void Deserialize(byte[] data)
    {
        using (MemoryStream memoryStream = new MemoryStream(data))
        {
            BinaryReader binaryReader = new BinaryReader(memoryStream);
            playerName = binaryReader.ReadString();
            playerScore = binaryReader.ReadInt32();
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        // 创建PlayerData对象
        PlayerData playerData = new PlayerData();
        playerData.playerName = "John";
        playerData.playerScore = 100;

        // 序列化对象
        byte[] serializedData = playerData.Serialize();

        // 反序列化对象
        PlayerData deserializedPlayerData = new PlayerData();
        deserializedPlayerData.Deserialize(serializedData);

        // 输出反序列化后的数据
        Console.WriteLine("Player Name: " + deserializedPlayerData.playerName);
        Console.WriteLine("Player Score: " + deserializedPlayerData.playerScore);
    }
}

这个示例演示了如何在C#中自定义序列化和反序列化方法,而不使用 BinaryFormatter。在 PlayerData 类中,我们定义了 SerializeDeserialize 方法来手动序列化和反序列化对象的数据。这样做可以更灵活地控制数据格式,并确保在不同平台和语言之间的兼容性。


6.3 答题示例

“在C#中虽然BinaryFormatter提供了便捷的序列化方案,但在实际开发中我们更倾向于自定义序列化实现,主要基于以下考虑:

  1. 跨平台/语言兼容性BinaryFormatter采用专有的二进制格式,依赖完整的.NET类型元数据,这使得它难以与其他技术栈(如Java、Go)互通。自定义协议(如Protobuf、JSON)通过明确的数据契约,能轻松实现跨语言通信,这在前后端分离或微服务架构中尤为重要。

  2. 性能与空间优化BinaryFormatter会序列化类型信息、程序集版本等额外元数据,导致数据体积膨胀。而手动实现(如示例中的BinaryWriter)可以针对性地只序列化必要字段,甚至采用压缩编码(如VarInt存储整数),大幅减少传输/存储开销。

  3. 安全与可控性BinaryFormatter存在反序列化漏洞(如OWASP警告),且无法灵活应对特殊需求(如字段加密、版本兼容)。自定义方案可以精确控制序列化过程,例如对敏感字段进行加密转换,或在版本迭代时添加字段映射逻辑。

  4. 协议演进支持:游戏开发中经常需要在不破坏兼容性的前提下迭代数据结构。自定义序列化可以实现版本号管理、可选字段处理等机制,而BinaryFormatter对schema变更的支持较弱。

实际项目中,我们通常会选择成熟的跨平台序列化库(如Protobuf、MessagePack)而非完全手动实现,但核心思想都是通过自定义协议来满足特定场景需求。”


6.4 关键词联想

  • 跨平台序列化:Protobuf、MessagePack、JSON
  • 性能优化:零拷贝(Zero-copy)、字段压缩(VarInt/zigzag)
  • 安全风险:反序列化漏洞、Binder劫持
  • 版本兼容:字段别名([DataMember(Name="oldName")])、可选字段
  • 高级技术:IL编织(如protobuf-net)、AOT编译支持
  • 对比方案:
    • 基于反射(如BinaryFormatter
    • 基于代码生成(如Protobuf)
    • 基于约定(如Newtonsoft.Json)
  • 工具链:Protocol Buffers、FlatBuffers、Bond


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏