Google.Protobuf 是 Google 官方推出的 Protocol Buffers(简称 Protobuf)的 C#/.NET 实现,是一种语言中立、平台无关、高效的序列化协议,核心用于结构化数据的序列化 / 反序列化,广泛应用于微服务通信、数据存储、跨语言交互等场景。
一、核心优势
相比 JSON/XML,Protobuf 有以下核心优势:
体积更小:二进制编码,数据压缩率极高(通常比 JSON 小 30%-90%);
速度更快:序列化 / 反序列化效率是 JSON 的数倍(无需解析文本,直接操作二进制);
强类型:基于 .proto 定义文件,编译后生成强类型代码,避免类型错误;
版本兼容:支持字段的向后 / 向前兼容,新增 / 废弃字段不影响老版本解析;
多语言支持:支持 C#、Java、C++、Python 等数十种语言,跨语言交互友好。
二、核心概念与工作流程
Protobuf 的使用分为 3 个核心步骤:
定义 .proto 文件:描述数据结构(字段、类型、规则);
编译 .proto 文件:通过 Protobuf 编译器(protoc)生成对应语言的代码(如 C# 类);
业务代码中使用:调用生成的代码完成序列化(对象→二进制)和反序列化(二进制→对象)。
三、详细使用教程(以 C# 为例)
步骤 1:环境准备
安装 NuGet 包:
bash
运行
Install-Package Google.Protobuf # 核心库
Install-Package Grpc.Tools # 包含 protoc 编译器(可选,若手动编译可不用)
下载 protoc 编译器(若未用 Grpc.Tools):
从 Protobuf 官网 下载对应平台的 protoc,配置环境变量。
步骤 2:定义 .proto 文件
.proto 是 Protobuf 的核心,用于声明数据结构,以下是完整示例(User.proto):
protobuf
// 指定 proto 版本(推荐 3,兼容性更好)
syntax = "proto3";
// 生成代码的命名空间(C# 专属)
option csharp_namespace = "MyApp.ProtoModels";
// 定义消息(对应 C# 中的类)
message User {
// 字段格式:[类型] [字段名] = [字段编号];
// 字段编号:1-15 占 1 字节,16-2047 占 2 字节,建议常用字段用 1-15
int32 id = 1; // 整型 ID
string name = 2; // 字符串名称
repeated string tags = 3; // 重复字段(对应 C# List<string>)
bool is_vip = 4; // 布尔值
double balance = 5; // 浮点型
UserType type = 6; // 枚举类型
Address address = 7; // 嵌套消息类型
}
// 定义枚举
enum UserType {
// 枚举值必须从 0 开始(默认值)
USER_TYPE_UNSPECIFIED = 0;
USER_TYPE_NORMAL = 1;
USER_TYPE_ADMIN = 2;
}
// 定义嵌套消息(也可单独定义)
message Address {
string province = 1;
string city = 2;
string detail = 3;
}
关键语法说明:
语法元素 说明
syntax = “proto3” 指定版本(proto2/proto3,proto3 更简洁,无默认值、required 等)
option csharp_namespace 生成 C# 代码的命名空间
message 定义数据结构(对应类)
repeated 标记重复字段(对应 C# 的 RepeatedField,可转 List)
enum 枚举类型,值必须从 0 开始
字段编号 唯一标识字段(序列化时用编号而非字段名),一旦发布不可修改
步骤 3:编译 .proto 生成 C# 代码
方式 1:手动编译(protoc 命令)
bash
运行
语法:protoc --csharp_out=[输出目录] [proto文件路径]
protoc --csharp_out=./Generated ./User.proto
方式 2:自动编译(Visual Studio 配置)
在 .proto 文件的属性中设置:
生成操作:Protobuf Compiler
编译工具:grpc_csharp_plugin(若仅用 Protobuf 无需 gRPC,选默认)
输出目录:./Generated
编译项目后,会自动生成 User.cs 文件,包含强类型的 User、UserType、Address 类。
步骤 4:序列化 / 反序列化实战
生成代码后,即可在业务代码中使用:
// 1. 创建对象并赋值
csharp
运行
using MyApp.ProtoModels;
using Google.Protobuf;
using System.IO;
var user = new User
{
Id = 1001,
Name = "张三",
IsVip = true,
Balance = 999.99,
Type = UserType.UserTypeNormal,
Address = new Address
{
Province = "广东省",
City = "深圳市",
Detail = "XX街道XX号"
}
};
// 添加重复字段(tags)
user.Tags.Add("会员");
user.Tags.Add("高消费");
// 2. 序列化:对象 → 二进制字节数组
byte[] bytes = user.ToByteArray();
// 或序列化到流
using (var stream = new MemoryStream())
{
user.WriteTo(stream);
bytes = stream.ToArray(); // 从流中获取字节数组
}
// 3. 反序列化:二进制字节数组 → 对象
User deserializedUser = User.Parser.ParseFrom(bytes);
// 或从流反序列化
using (var stream = new MemoryStream(bytes))
{
deserializedUser = User.Parser.ParseFrom(stream);
}
// 4. 验证结果
Console.WriteLine($"ID: {deserializedUser.Id}");
Console.WriteLine($"Name: {deserializedUser.Name}");
Console.WriteLine($"Tags: {string.Join(",", deserializedUser.Tags)}");
Console.WriteLine($"Address: {deserializedUser.Address.Province}-{deserializedUser.Address.City}");
**四、核心 API 解析**
Google.Protobuf 提供了核心基类和方法,支撑序列化 / 反序列化:
API 类 / 方法 作用
IMessage 所有生成的消息类都实现此接口,定义序列化 / 反序列化核心方法
ToByteArray() 序列化对象为字节数组(IMessage 接口方法)
WriteTo(Stream) 序列化对象到流(高效,适合大对象)
Parser 每个消息类的静态解析器,用于反序列化
ParseFrom(byte[]) 从字节数组反序列化(Parser 方法)
ParseFrom(Stream) 从流反序列化(推荐,减少内存拷贝)
RepeatedField<T> 重复字段的容器,支持 Add/Clear/Count 等方法,可转 List<T>
五、高级特性
1. 版本兼容性
Protobuf 天生支持版本兼容,核心规则:
新增字段:老版本解析时会忽略新增字段,新版本可正常解析老数据;
废弃字段:标记为 reserved(如 reserved 8;),避免复用字段编号;
字段类型兼容:如 int32 和 int64 可兼容(需确保数值范围)。
示例:
protobuf
// 废弃字段 8,禁止复用
message User {
reserved 8;
// 新增字段 9,不影响老版本解析
string email = 9;
}
2. 自定义序列化选项
忽略默认值:proto3 中默认值(如 int=0、string="")不会序列化,减少体积;
JSON 互转:Google.Protobuf 支持 Protobuf ↔ JSON 转换(需安装 Google.Protobuf.Json):
csharp
运行
// Protobuf → JSON
string json = user.ToString();
// JSON → Protobuf
User userFromJson = User.Parser.ParseJson(json);
3. 大对象处理
对于超大对象,推荐使用 WriteTo(Stream) 和 ParseFrom(Stream),避免一次性加载字节数组到内存:
csharp
运行
// 序列化大对象到文件流
using (var fileStream = File.Create("user.data"))
{
largeUser.WriteTo(fileStream);
}
// 从文件流反序列化大对象
using (var fileStream = File.OpenRead("user.data"))
{
var largeUser = User.Parser.ParseFrom(fileStream);
}
六、注意事项
字段编号不可修改:一旦发布,字段编号是序列化的核心标识,修改会导致反序列化失败;
类型匹配:C# 类型与 proto 类型需严格匹配(如 proto int32 → C# int,uint64 → ulong);
空值处理:proto3 无 null 概念,字符串默认空、数字默认 0,需通过 HasField 检查字段是否显式设置(仅 proto2 支持,proto3 需自定义标记);
性能优化:
重复字段优先用 RepeatedField<T> 而非 List;
大对象用流序列化,避免字节数组拷贝;
高频序列化场景可复用 Parser 和 MemoryStream。
七、适用场景
微服务间 RPC 通信(如 gRPC 底层基于 Protobuf);
高性能数据存储(如缓存、日志、数据库二进制字段);
跨语言数据交互(如 C# 服务与 Java 服务通信);
移动端 / 物联网设备通信(带宽有限,需小体积数据)。
总结
Google.Protobuf 是高效、强类型、跨平台的序列化方案,核心是通过 .proto 定义数据结构,编译生成强类型代码,再通过 ToByteArray()/ParseFrom() 完成序列化 / 反序列化。相比 JSON/XML,它在性能和体积上有显著优势,是高性能分布式系统的首选序列化协议。

3万+

被折叠的 条评论
为什么被折叠?



