当前位置: 首页 > news >正文

Protocol Buffers .NET 运行时从核心 API 到工程实战

1. 全局地图:三个关键命名空间

  • Google.Protobuf(核心)

    • 序列化/反序列化:CodedInputStreamCodedOutputStream
    • JSON 互转:JsonFormatterJsonParser
    • 字节容器:ByteString
    • 解析与扩展:MessageParser<T>MessageExtensions
    • 约束与异常:ProtoPreconditionsInvalidProtocolBufferExceptionInvalidJsonException
  • Google.Protobuf.Collections(集合)

    • RepeatedField<T>:repeated 字段容器(不允许 null,支持深拷贝)
    • MapField<TKey, TValue>:map 字段容器与对应 Codec
  • Google.Protobuf.Reflection(反射)

    • 描述符体系:FileDescriptorMessageDescriptorFieldDescriptorEnumDescriptorServiceDescriptor
    • oneof 辅助:OneofDescriptorOneofAccessor
    • 原始名注解:OriginalNameAttribute
    • 类型仓库:TypeRegistry

此外,WellKnownTypesGoogle.Protobuf.WellKnownTypes)提供常用内置类型:TimestampDurationAnyFieldMaskStruct、各类 *Value 包装器等。

2. 消息的基本功:二进制与 JSON

2.1 二进制序列化/反序列化

using Google.Protobuf;
using System.IO;var person = new Person { Id = 1, Name = "Ada" };// 写二进制
using var ms = new MemoryStream();
person.WriteTo(ms);
var bytes = ms.ToArray();// 读二进制
var parsed = Person.Parser.ParseFrom(bytes);
// 或者:new CodedInputStream(bytes) / CodedOutputStream
  • 推荐:优先使用 Message.WriteTo / Parser.ParseFrom,底层已优化。
  • 异常:格式错误会抛出 InvalidProtocolBufferException,请捕获并统一处理。

2.2 JSON 互转(调试、网关、日志常用)

using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using Google.Protobuf.Reflection;
using Google.Protobuf.Json;
using Google.Protobuf.JsonParser; // 在旧版本命名空间为 Google.Protobuf// 序列化为 JSON
var json = JsonFormatter.Default.Format(person);// 从 JSON 解析
var parsed2 = JsonParser.Default.Parse<Person>(json);
  • JsonFormatter.SettingsJsonParser.Settings 可自定义大小写、默认值输出、枚举样式等。
  • 与跨语言协作:Protobuf JSON 规范与普通 JSON 略有差异(例如 TimestampDuration 的格式);使用官方工具避免手写转换。

3. 集合类型:Repeated 与 Map 的那些细节

3.1 RepeatedField

  • 表示 .proto 中的 repeated 字段。
  • 不允许 null 元素,支持 AddAddRange、索引访问、深拷贝。
person.Phones.Add(new PhoneNumber { Number = "123" });
person.Tags.AddRange(new[] { "ai", "ml" });

3.2 MapField<TKey, TValue>

  • 表示 .proto 中的 map<K, V> 字段。
  • 语义与 Dictionary<K,V> 接近,但具备 Protobuf 序列化行为与 Codec 支持。
var booth = new MerchBooth();
booth.Items["Signed T-Shirt"] = new MerchItem { Price = 99 };

实战建议:业务层可转换为 Dictionary 做 LINQ 处理,但在消息边界(序列化层)请保留 MapField,避免不必要的拷贝。

4. 反射 API:动态场景的瑞士军刀

当消息类型在编译期未知(插件、脚本、网关、动态转换)时,反射很有用:

var md = Person.Descriptor;                         // MessageDescriptor
foreach (var fd in md.Fields.InDeclarationOrder())  // 字段遍历
{Console.WriteLine($"{fd.Name} : {fd.FieldType}");
}// 获取/设置字段(反射方式)
var msg = new DynamicMessage(md);
var nameField = md.FindFieldByName("name");
msg[nameField] = "Turing";
  • 描述符族:FileDescriptor / MessageDescriptor / FieldDescriptor / EnumDescriptor
  • Oneof:用 OneofDescriptor + OneofAccessor 反射地清空或获取当前 case
  • TypeRegistry:Any/JSON 解析时注册已知类型,支持跨消息类型反序列化

5. Well-Known Types:时间、动态结构与 Any

5.1 时间类型

  • Timestamp(UTC 纪元秒+纳秒)、Duration(时长)
  • 搭配 TimeExtensions 在 BCL 类型与 Protobuf 类型间转换
using Google.Protobuf.WellKnownTypes;var now = Timestamp.FromDateTime(DateTime.UtcNow);
var dt = now.ToDateTime(); // back to DateTime (UTC)

5.2 结构化动态值

  • Struct / Value / ListValue:用于“半结构化 JSON”存储
  • 场景:配置中心、动态 payload、扩展字段

5.3 Any:承载任意消息

var any = Any.Pack(person);            // 包装
var ok = any.TryUnpack(out Person p);  // 解包

TypeRegistry 配合,能在 JSON 场景保持类型信息。

5.4 包装器(Wrapper)类型

  • Int32ValueStringValue 等:表达“可空的标量字段”(与 C# 的 int?string? 语义对齐)
  • repeated 中不允许 nullmap value 允许 null

6. 错误与健壮性:异常、前置条件、诊断

  • 反序列化异常:

    • InvalidProtocolBufferException:二进制格式错误
    • InvalidJsonException:JSON 格式错误
  • 参数校验:ProtoPreconditions(库内部使用,工程可借鉴同样思路)

  • 诊断输出:实现 ICustomDiagnosticMessage 可自定义 ToString() 风格日志

7. 性能与工程实践

7.1 避免多余的分配与拷贝

  • 优先使用 WriteTo(Stream)/ParseFrom(ReadOnlySpan<byte>) 等流/Span API
  • 大对象/热点链路可复用缓冲区,或使用 CodedOutputStream 的高阶控制

7.2 JSON 只做边界转换

  • 在服务内传递请使用二进制;对外(浏览器/HTTP 调试)再转 JSON
  • JSON 设置统一化(大小写、默认值、枚举文本)以减轻前后端契约摩擦

7.3 与 Domain 模型解耦

  • Protobuf 类是“数据容器”,避免将领域逻辑写进生成类
  • 用组装器/Mapper(如 Mapster/手写)在 Protobuf DTO 与领域模型间转换

7.4 版本演进与兼容

  • 遵守字段编号不变、更改只增不删的基本规则
  • 对“不确定是否需要”的字段优先用 Wrapper 表达“未设置”与“空值”的区分

8. 常用代码片段速查

从文件读写:

using var fs = File.OpenRead("data.pb");
var data = Person.Parser.ParseFrom(fs);using var ws = File.Create("data.pb");
data.WriteTo(ws);

JSON 设置:

var formatter = new JsonFormatter(new JsonFormatter.Settings(formatDefaultValues: false));
var json = formatter.Format(data);var parser = new JsonParser(new JsonParser.Settings(ignoringUnknownFields: true));
var fromJson = parser.Parse<Person>(json);

Repeated 与 Map:

data.Tags.Add("x");
data.Attributes["lang"] = "en";

反射读取字段:

var fd = Person.Descriptor.FindFieldByNumber(1); // 按编号找字段
var val = data.GetType().GetProperty(fd.CsharpOptions.PropertyName)?.GetValue(data);

Any + TypeRegistry + JSON:

var reg = TypeRegistry.FromMessages(Person.Descriptor);
var jf = new JsonFormatter(new JsonFormatter.Settings(typeRegistry: reg));var any = Any.Pack(data);
var jsonAny = jf.Format(any);var jp = new JsonParser(new JsonParser.Settings(typeRegistry: reg));
var anyParsed = jp.Parse<Any>(jsonAny);
anyParsed.Unpack(out Person p2);

结语

  • Google.Protobuf核心 API打通序列化/JSON/反射三条链路;
  • CollectionsRepeatedFieldMapField 正确承载集合;
  • WellKnownTypes 解决“时间/可空/动态结构/Any”的常见痛点;
  • ReflectionTypeRegistry 支撑网关、插件化、跨语言的高级场景。

把 Protobuf 当作“跨语言、高性能、强约束的传输与存储层”,把业务语义留给领域模型,让你的 .NET 服务既快又稳、演进无痛。

http://www.dtcms.com/a/393164.html

相关文章:

  • DFT教程 part1 VASP安装与学习推荐
  • 泛函 Φ(u) 驻点所满足的偏微分方程与自然边界条件
  • 基于springboot的健康饮食营养管理系统
  • C语言入门指南:联合体与枚举
  • JS逆向 -去哪儿滑块
  • C++包装器(Wrapper)概述
  • java后端工程师进修ing(研一版‖day47)
  • 小谈:物联网(IoT)与工业传感器技术
  • python标准库
  • 01 Tasking软件安装及新建工程
  • ​​[硬件电路-288]: 单路双输入异或门(门控开关:两个同时为1,输出为1)NC7SZ86L6X 功能概述与管脚定义
  • 声明式事务4
  • 速通ACM省铜第十天 赋源码(A Good Problem和Make It Beautiful和Gellyfish and Baby‘s Breath)
  • 寰宇光锥舟
  • 如何安全的计算softmax?
  • 第一部分:基础架构与入门
  • [Windows] Cencrack在线工具包6.52
  • 算法基础篇(2)模拟
  • Go基础:Go语言结构体(Struct)和接口(Interface)详解
  • 【计算机毕业设计】基于生成对抗网络的动作与表情一致性动漫角色生成算法系统​
  • html5 做个人网页识芯平夹回拼翘
  • 开收价均值策略
  • 【大模型部署】Ollama部署gguf模型
  • Coze源码分析-资源库-删除工作流-前端源码-核心组件
  • 机器学习实战第八章 降维
  • 2025年csp-j真题和解析
  • C++ STL map 深度解析:从原理到实战的全方位指南
  • 【EKF组合导航例程】MATLAB代码,15维状态量、3维观测量的组合导航,滤波使用EKF(扩展卡尔曼滤波)。附下载链接
  • word文档怎么根据大纲拆分章节
  • 【Modbus】Modbus协议基础知识详解