google.protobuf必须基于.proto文件生成代码,不支持属性标注;protobuf-net支持运行时注解和动态类型。两者不可混用,否则易出现解析失败、字段编号不匹配等错误。

直接用 Google.Protobuf(即官方 protobuf 库)在 C# 中做序列化/反序列化,和用 protobuf-net 是两套完全不同的体系——前者必须基于 .proto 文件生成代码,后者支持运行时注解。如果你看到“解析失败”“找不到类型”“Unknown field number”这类错误,大概率是混用了两者,或没走对生成流程。
proto 文件必须先编译成 C# 类
Google.Protobuf 不接受手动加 [ProtoContract] 的类;它只认由 protoc 编译器生成的代码。不生成,就无法反序列化。
- 安装
protoc:从 GitHub releases 下载对应平台的protoc二进制(如protoc-24.4-win64.zip),解压后把bin/protoc.exe加入系统 PATH - 准备
person.proto(注意必须是 proto3 语法):syntax = "proto3"; package example; message Person { int32 id = 1; string name = 2; repeated string phone = 3; } - 执行生成命令:
protoc --csharp_out=. person.proto,会生成Person.cs - 将生成的
.cs文件加入项目,并引用 NuGet 包:Google.Protobuf
Deserialize 不能直接传任意 T,必须是生成的 Message 类型
你不能写 Serializer.Deserialize<person>(data)</person>(那是 protobuf-net 的 API)。Google.Protobuf 的反序列化方法是实例方法,且类型必须继承自 Google.Protobuf.IMessage。
- 正确写法:
var p = Person.Parser.ParseFrom(data);—— 每个生成的 message 类都自带静态Parser属性 - 如果数据来自网络流,用
ParseDelimitedFrom(支持长度前缀帧):Person.Parser.ParseDelimitedFrom(stream) - 不要试图用反射或泛型包装
Parser,因为Parser是具体类型的静态成员,不是泛型接口 - 若需动态类型,得用
Google.Protobuf.Reflection.MessageDescriptor+DynamicMessage,但性能差、易出错,仅用于调试或元数据场景
常见报错:“Invalid wire-type” 或 “Unexpected end-group tag”
这不是代码写错了,而是数据格式不匹配——90% 情况下,是序列化端和反序列化端用的不是同一版 .proto,或传输时截断/粘包/编码污染了二进制流。
- 确认双方使用完全相同的
.proto文件(包括syntax、package、字段编号、repeated修饰符) - 检查是否误把 UTF-8 字符串当二进制传:比如用
Encoding.UTF8.GetString(bytes)再传给ParseFrom,会导致乱码字节 → 直接传原始byte[] - 如果是 socket 通信,必须处理粘包:Protobuf 本身不带分帧,要么自己加长度头(推荐),要么用
ParseDelimitedFrom(它会读取 varint 前缀) - 用
TryParseFrom替代ParseFrom做容错,避免因单条脏数据崩掉整个服务
与 protobuf-net 的关键区别不能忽略
如果你正在迁移旧项目,或同时接触两个库,这几个点必须刻在脑子里:
-
Google.Protobuf:强依赖.proto→ 生成代码 → 运行时零反射;适合跨语言、协议长期稳定场景 -
protobuf-net:支持属性标注([ProtoMember(1)])、运行时模型构建、甚至可序列化无源码的第三方类型;适合纯 .NET 内部通信、快速迭代 - 字段编号冲突后果不同:
Google.Protobuf中编号是协议契约,改了就是不兼容;protobuf-net虽也建议别动,但反序列化时缺失字段不会报错,只是跳过 - 空值处理不同:
Google.Protobuf的string字段永远非 null(默认空字符串),而protobuf-net可配置为允许 null
最常被忽略的一点:Google.Protobuf 默认不序列化默认值(如 int32 id = 0),而 protobuf-net 会——这在做 diff 或日志比对时可能引发隐性差异。











