C#序列化技术:BinaryFormatter、System.Text.Json、Protocol Buffers
在 C# 开发中,序列化是对象与数据之间桥梁式的存在,广泛应用于数据存储、网络传输、远程通信等场景。
序列化:将数据结构或对象状态转换为可存储或可传输的格式的过程。这个过程将内存中的对象转换为字节序列,可以写入文件、数据库或通过网络传输。
反序列化:序列化的逆过程,将序列化的数据重新构建为内存中的对象,恢复其原始状态和结构。
1. BinaryFormatter —— 经典但已被淘汰的序列化工具
✅ 简介
BinaryFormatter
是 .NET Framework 中最早期的序列化工具之一,能将对象直接转为二进制流,适合本地持久化、进程间通信等用途。
❌ 存在的问题
-
存储数据包含完整类型信息,存在反序列化攻击风险
-
序列化后的数据与平台强绑定,不具备跨语言能力(比如反序列化时,程序集名称必须和系列化时一致)
-
Microsoft 在 .NET 5+ 明确弃用它,且在.NET 7中完全禁用默认使用
2. System.Text.Json —— 现代 .NET 推荐方案
✅ 简介
System.Text.Json
是 .NET Core 3.0 起官方推出的高性能 JSON 序列化库,替代了之前广泛使用的 Newtonsoft.Json
,性能更好、原生支持 Span<T>
与 UTF-8
。
✅ 特点
-
人类可读的 JSON 格式,调试友好
-
无需标记属性,默认即可工作
-
性能优于 Newtonsoft.Json
-
支持 ASP.NET Core 原生绑定
✅ 适合场景
-
Web API 请求/响应体序列化
-
日志记录、配置文件
-
跨平台、语言间 JSON 通信
3. Protocol Buffers(Protobuf)—— 高性能二进制序列化
✅ 简介
Protobuf 是 Google 开发的跨平台、高性能、紧凑型二进制序列化协议,适用于对性能敏感、带宽有限的场景。它需要定义 .proto
文件,然后用工具生成 C# 类。
示例 .proto 文件:
生成并使用 C# 代码:
using var stream = File.Create("person.pb"); Person person = new Person { Name = "Charlie", Age = 28 }; person.WriteTo(stream); // 反序列化 using var input = File.OpenRead("person.pb"); Person parsed = Person.Parser.ParseFrom(input);
-
二进制编码,占用空间小,速度极快
-
强类型,严格结构控制
-
向后/向前兼容性好(字段编号)
-
跨语言支持广泛(Java、Python、C++、Go 等)
✅ 适合场景
-
微服务通信(如 gRPC)
-
嵌入式设备、移动端
-
高吞吐、高并发应用
4. 总结
大小和速度对比测试
(1)windows安装 Protocol Buffers 编译器 (protoc
)
- 前往官网下载编译器 zip 包
- 解压后,把
bin
文件夹路径加入到环境变量中
(2)用 protoc
生成 C# 文件:
protoc --csharp_out=. Person.proto
(3)代码

using System.Diagnostics; using System.Text.Json; using Google.Protobuf; public class Person { public string Name { get; set; } public int Age { get; set; } } class Program { const int Iterations = 10000; static void Main() { var person = new Person { Name = "Alice", Age = 30 }; var personProto = new PersonMessage { Name = "Alice", Age = 30 }; Console.WriteLine("Running serialization benchmarks...\n"); Benchmark("System.Text.Json", () => { return JsonSerializer.SerializeToUtf8Bytes(person); }, bytes => { JsonSerializer.Deserialize<Person>(bytes); }); Benchmark("Protocol Buffers", () => { using var ms = new MemoryStream(); personProto.WriteTo(ms); return ms.ToArray(); }, bytes => { PersonMessage.Parser.ParseFrom(bytes); }); } static void Benchmark(string name, Func<byte[]> serialize, Action<byte[]> deserialize) { GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); byte[] result = null; Stopwatch sw = new Stopwatch(); // Serialize sw.Start(); for (int i = 0; i < Iterations; i++) { result = serialize(); } sw.Stop(); long serializeTime = sw.ElapsedMilliseconds; // Deserialize sw.Restart(); for (int i = 0; i < Iterations; i++) { deserialize(result); } sw.Stop(); long deserializeTime = sw.ElapsedMilliseconds; double avgSerialize = serializeTime * 1000.0 / Iterations; // 微秒 double avgDeserialize = deserializeTime * 1000.0 / Iterations; // 微秒 Console.WriteLine($"[{name}]"); Console.WriteLine($" Serialized Size: {result.Length} bytes"); Console.WriteLine($" Avg Serialize Time: {avgSerialize:F2} µs"); Console.WriteLine($" Avg Deserialize Time: {avgDeserialize:F2} µs\n"); } }
Running serialization benchmarks... [System.Text.Json] Serialized Size: 25 bytes Avg Serialize Time: 5.50 μs Avg Deserialize Time: 0.80 μs [Protocol Buffers] Serialized Size: 9 bytes Avg Serialize Time: 0.70 μs Avg Deserialize Time: 0.30 μs
总结
特性 | BinaryFormatter | System.Text.Json | Protocol Buffers |
---|---|---|---|
数据格式 | 二进制(含类型信息) | JSON 文本 | 紧凑型二进制格式 |
性能 | 中 | 中-高 | 高 |
安全性 | ❌ 存在严重安全风险 | ✅ 安全 | ✅ 安全 |
可读性 | ❌ | ✅ 人类可读 | ❌ |
跨平台/跨语言支持 | ❌ | ⚠ 有限(需字段兼容) | ✅ 强 |
推荐使用 | ❌ 不推荐 | ✅ 推荐 | ✅ 推荐 |
如何选择?
需求场景 | 推荐序列化技术 |
---|---|
高性能通信、跨语言 | ✅ Protobuf |
Web 服务、API 接口 | ✅ System.Text.Json |
本地临时存储(可读性优先) | ✅ System.Text.Json |
旧项目中偶尔仍需使用 | ⚠ BinaryFormatter(需谨慎) |