從零到一上手 Protocol Buffers用 C# 打造可演進的通訊錄

一、為什么是 Protobuf(而不是 XML/自定義字符串/.NET 二進制序列化)

在需要把結構化對象持久化或跨進程/跨語言傳輸時,常見方案各有痛點:

  • BinaryFormatter 等 .NET 二進制序列化:對類型簽名與版本極其脆弱、體積偏大,且跨語言互通性差
  • 自定義分隔字符串:一次性編碼/解析成本高,健壯性與可讀性差。
  • XML:可讀但冗長、編解碼開銷大。

Protocol Buffers(Protobuf) 的優勢在于:
用一個 .proto 文件聲明消息結構,protoc 生成高效的 C# 類型,提供自動的二進制編碼/解碼;并且天然支持演進(老代碼可讀新消息、反之亦然)。

二、工程與示例骨架

本文示例是一個命令行通訊錄工具:

  • 可新增聯系人并寫入文件;
  • 可讀取文件并打印聯系人列表。

完整示例(Program.csaddressbook.proto 等)可按本文步驟自行創建;若你使用官方示例倉庫,也能在 examplescsharp/src/AddressBook 目錄中找到等價實現。

三、定義協議:addressbook.proto

syntax = "proto3";
package tutorial;import "google/protobuf/timestamp.proto";// 覆蓋 C# 默認命名空間(否則取 package 名)
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";message Person {string name = 1;int32  id   = 2;   // 唯一 IDstring email = 3;enum PhoneType {PHONE_TYPE_UNSPECIFIED = 0;PHONE_TYPE_MOBILE = 1;PHONE_TYPE_HOME   = 2;PHONE_TYPE_WORK   = 3;}message PhoneNumber {string   number = 1;PhoneType type  = 2;}repeated PhoneNumber phones = 4;google.protobuf.Timestamp last_updated = 5;
}message AddressBook {repeated Person people = 1;
}

要點速記

  1. package 防沖突;csharp_namespace 可定制命名空間。
  2. = 1/2/…標簽號(wire tag),1–15 編碼更省字節,優先留給常用repeated 字段。
  3. 未賦值字段使用類型默認值(數字 0、布爾 false、字符串空串、枚舉 0 值)。
  4. repeated有序動態數組;集合類型在 C# 中為 RepeatedField<T>(只讀集合,支持增刪元素)。
  5. 嵌套 messageenum 提升結構清晰度;可以引入標準類型(如 Timestamp)。

四、編譯生成 C# 代碼

安裝好 protoc 后執行:

protoc -I=$SRC_DIR --csharp_out=$DST_DIR $SRC_DIR/addressbook.proto

輸出 Addressbook.cs,其中包含:

  • 靜態 Addressbook(元數據);
  • AddressBookPersonPerson.Types.PhoneNumber 類;
  • Person.Types.PhoneType 枚舉。

注意RepeatedField<T> 是只讀集合屬性,不能整體替換,只能調用 Add/Remove/Clear 等方法修改元素。

五、寫與讀:序列化 / 反序列化

1)寫入

using System.IO;
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using Google.Protobuf.Examples.AddressBook;
using static Google.Protobuf.Examples.AddressBook.Person.Types;var john = new Person {Id = 1234,Name = "John Doe",Email = "jdoe@example.com",LastUpdated = Timestamp.FromDateTime(DateTime.UtcNow)
};
john.Phones.Add(new PhoneNumber { Number = "555-4321", Type = PhoneType.HOME });var book = new AddressBook();
book.People.Add(john);using var output = File.Create("addressbook.binpb");
book.WriteTo(output); // 二進制高效寫入

2)讀取

using var input = File.OpenRead("addressbook.binpb");
var parsed = AddressBook.Parser.ParseFrom(input);foreach (var p in parsed.People) {Console.WriteLine($"{p.Id} | {p.Name} | {p.Email}");foreach (var ph in p.Phones) {Console.WriteLine($"  - {ph.Type}: {ph.Number}");}
}

六、生成代碼的常見模式

  • 屬性訪問:普通字段直接 get/set;repeated 用集合操作。
  • using static 簡化枚舉/嵌套名using static Person.Types; 之后可寫 PhoneType.HOME
  • Debug 與 JSON:調試可用 ToString()(等同 JsonFormatter 的簡版),生產落盤/傳輸請使用二進制 WriteTo/ParseFrom不要把調試輸出當數據格式使用。

七、向前/向后兼容的演進法則

演進 .proto 需遵守:

  1. 不要修改已有字段的標簽號
  2. 可以刪除字段;
  3. 可以新增字段,但必須使用從未使用過的新標簽號(包括曾被刪除的號也不能復用)。

這樣:

  • 舊程序讀新消息:會忽略看不懂的新字段;
  • 新程序讀舊消息:新加字段會呈現其默認值(比如空串/0/false)。

小貼士:把最常用/可能 repeated 的字段優先安排在 1–15,可觀節省體積。

八、反射(Reflection)一鍵遍歷任意消息

當你需要寫通用邏輯(如通用打印、對比、轉其它文本格式)時,反射很有用:

using Google.Protobuf;
using Google.Protobuf.Reflection;static void DumpTopLevel(IMessage msg) {var desc = msg.Descriptor;foreach (var f in desc.Fields.InDeclarationOrder()) {var val = f.Accessor.GetValue(msg);Console.WriteLine($"#{f.FieldNumber} {f.Name} = {val}");}
}

九、最佳實踐與易踩坑

  • 不要把生成的 Protobuf 類當領域模型的“上帝類”:它們是數據載體。復雜業務建議用封裝類包一層,便于隱藏實現細節與組合額外行為。
  • 文件擴展名約定:二進制 .binpb、文本 .txtpb、JSON .json,保持項目內一致性。
  • 調試輸出 ≠ 數據格式:日志用調試格式即可;要傳輸/落盤請用二進制或顯式 TextFormat/JSON
  • 版本演進前先留余量:給可能增長的 repeated 預留 1–15;對未來“也許有用”的字段先不上線,避免隨后改 tag。
  • 單元測試:比較對象請用值相等Equals/字段比對)而非字節流相等;序列化非確定性,不同運行時/版本字節序可能不同但語義相同

十、把示例跑起來(最短路徑)

  1. 安裝 Google.Protobuf NuGet 與 protoc
  2. 寫好 addressbook.proto
  3. 執行 protoc --csharp_out 生成 Addressbook.cs
  4. 在項目中引用生成文件與 Google.Protobuf
  5. 按第 節代碼寫入/讀取,驗證通訊錄功能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/99213.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/99213.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/99213.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

計算機網絡(三)網絡層

三、網絡層網絡層是五層模型中的第三層&#xff0c;位于數據鏈路層和傳輸層之間。它的核心任務是實現數據包在不同網絡之間&#xff08;跨網絡&#xff09;的邏輯傳輸。網絡層的數據傳輸單位是數據報&#xff08;Datagram&#xff09;或數據包&#xff08;Packet&#xff09;。…

互聯網大廠Java面試實錄:從基礎到微服務全棧技術答疑

互聯網大廠Java面試實錄&#xff1a;從基礎到微服務全棧技術答疑 本文以電商場景為背景&#xff0c;展現一場互聯網大廠Java開發職位的面試過程。嚴肅的面試官與搞笑的水貨程序員謝飛機展開三輪技術問答&#xff0c;涵蓋Java SE、Spring Boot、數據庫、微服務、安全以及CI/CD等…

StringBuilder 深度解析:數據結構與擴容機制的底層細節

文章目錄 前言 一、數據結構&#xff1a;不止是簡單的字符數組 1. 核心成員變量&#xff08;定義在 AbstractStringBuilder 中&#xff09; 2. 構造器與初始容量 二、擴容機制&#xff1a;從 "不夠用" 到 "換大容器" 的全過程 步驟 1&#xff1a;計算…

Elasticsearch面試精講 Day 17:查詢性能調優實踐

【Elasticsearch面試精講 Day 17】查詢性能調優實踐 在“Elasticsearch面試精講”系列的第17天&#xff0c;我們聚焦于查詢性能調優實踐。作為全文檢索與數據分析的核心引擎&#xff0c;Elasticsearch的查詢性能直接影響用戶體驗和系統吞吐能力。在高并發、大數據量場景下&…

WPF 數據綁定模式詳解(TwoWay、OneWay、OneTime、OneWayToSource、Default)

在WPF中&#xff0c;數據綁定模式&#xff08;Binding Mode&#xff09;用于指定數據流的方向。常見的模式有TwoWay、OneWay、OneTime、OneWayToSource和Default。TwoWay&#xff08;雙向綁定&#xff09;&#xff1a;數據從源&#xff08;通常是ViewModel或數據上下文&#xf…

使用 NVIDIA Dynamo 部署 PD 分離推理服務

1 Dynamo 介紹 NVIDIA Dynamo 是一個開源的模塊化推理框架&#xff0c;用于在分布式環境上實現生成式 AI 模型的服務化部署。Dynamo 通過動態資源調度、智能路由、內存優化與高速數據傳輸&#xff0c;無縫擴展大型 GPU 集群之間的推理工作負載。 Dynamo 采用推理引擎無關的設…

答題卡識別改分項目

目錄 核心思路 分步實現與代碼解析 1. 環境準備與工具函數定義 2. 圖片預處理 3. 輪廓提取與篩選 3. 輪廓提取與篩選 4. 透視變換&#xff08;矯正傾斜答題卡&#xff09; 5. 閾值處理&#xff08;突出填涂區域&#xff09; 6. 提取選項圓圈輪廓 7. 選項輪廓排序&…

Python爬蟲實戰:研究Pandas,構建新浪網股票數據采集和分析系統

1. 系統概述 股票數據分析系統旨在通過自動化手段獲取市場數據,進行深度分析,輔助投資決策。本系統主要包含以下核心模塊: 數據爬取模塊:從新浪財經獲取股票列表、基本信息及歷史交易數據 數據處理模塊:清洗原始數據,處理缺失值與異常值,計算技術指標 分析可視化模塊:…

【C++STL】list的詳細用法和底層實現

&#x1f31f;個人主頁&#xff1a;第七序章 &#x1f308;專欄系列&#xff1a;C&#xff0b;&#xff0b; 目錄 ??前言&#xff1a; &#x1f308;一&#xff1a;介紹 &#x1f308;二&#xff1a;list的創建 ??基本框架 &#x1f319;節點類 &#x1f319;構造函…

AI大模型開發(多模態+提示詞)

接著之前的例子&#xff0c;繼續測試模型對話&#xff0c;今天主要測試多模態加上系統提示詞。 一.多模態 多模態方法&#xff0c;主要添加了對圖片的測試。 public String chatWithMessage(UserMessage userMessage){ChatResponse chatResponse qwenChatModel.chat(userMess…

Qt程序單獨運行報錯問題

Qt程序單獨運行報錯問題介紹問題原因分析解決方案&#xff08;從最佳實踐到臨時方法&#xff09;方法一&#xff1a;使用 windeployqt 工具&#xff08;最推薦、最規范&#xff09;方法二&#xff1a;臨時修改系統 PATH&#xff08;適合開發調試&#xff09;方法三&#xff1a;…

Flask學習筆記(二)--路由和變量

一、路由Flask支持兩種路由1、使用route()裝飾器將URL綁定到函數app.route(/hello)def hello_world():return hello world2、使用應用程序對象的add_url_rule()函數def hello_world():return hello worldapp.add_url_rule(/, hello, hello_world)二、變量規則Flask開發中&#…

Skywalking告警配置+簡易郵件告警應用配置(保姆級)

Skywalking告警配置簡易郵件告警應用配置前言&#xff1a; 前文&#xff1a;SkyWalking Elasticsearch8 容器化部署指南&#xff1a;國內鏡像加速與生產級調優_skywalkinges-CSDN博客 ? SKywalking Agent配置Oracle監控插件安裝指南-CSDN博客 Skywalking版本&#xff1a;V10.…

無人機如何實現圖傳:從原理到實戰的全景解讀

無人機圖傳的工作不是簡單地把鏡頭的數據直接“丟”到一個屏幕上&#xff0c;而是一個由編碼、傳輸、解碼三段組成的系統。首先是視頻編碼&#xff1a;攝像頭采集的原始畫面通常需要經過編解碼器壓縮&#xff0c;常見標準包括H.264、H.265和VP9等。壓縮的目的是減少數據量&…

AS32S601在軌重構(OTA)方案的優化與分析

摘要在軌重構&#xff08;OTA&#xff09;技術因其在航天、工業控制、物聯網等領域的高可靠性和持續服務需求而備受關注。本文以國科安芯推出的AS32S601芯片為研究對象&#xff0c;深入分析其OTA方案的設計原理、技術細節及優化策略&#xff0c;并結合芯片的硬件特性&#xff0…

修復Android studio的adb無法連接手機問題

復制下面的內容到一個文本txt里面然后把里面的Android studio路徑和sdk路徑改成你自己的&#xff0c;然后改成把.txt改成bat 右鍵管理員運行 echo off REM Deep Fix for "Couldnt terminate the existing process" error REM This script will completely reset ADB …

css優化都有哪些優化方案

CSS 優化其實可以分成幾個層面&#xff1a;性能優化、可維護性優化、兼容性優化以及用戶體驗優化。這里我幫你梳理一份比較系統的 CSS 優化方案清單&#xff0c;方便你參考&#xff1a;&#x1f539; 一、加載性能優化減少 CSS 文件體積壓縮 CSS&#xff08;去掉空格、換行、注…

vue,uniapp 實現卷簾對比效果

需求&#xff1a;兩張圖重疊放在一起&#xff0c;拖動分割線實現卷簾對比效果&#xff0c;如圖一、vue2代碼 <template><div class"main"><div class"img-comparison" mousedown"startSlide"><img class"before"…

【筆記】空氣彈簧概述、剛度調節原理

參考鏈接&#xff1a;汽車底盤空氣懸架關鍵零部件之空氣彈簧 1.概述 汽車空氣彈簧&#xff08;Air Spring&#xff09;是一種以“壓縮空氣”作為彈性介質的懸架元件&#xff0c;用來取代傳統鋼制螺旋彈簧或鋼板彈簧。它在乘用車、客車、重卡及軌道交通上越來越普及&#xff0…

UDP Socket 進階:從 Echo 到字典服務器,學會 “解耦” 網絡與業務

開篇&#xff1a;從 “回顯” 到 “字典”&#xff0c;核心變在哪&#xff1f;上一篇我們實現了 Echo 服務器 —— 網絡層和業務層是 “綁死” 的&#xff1a;網絡層收到數據后&#xff0c;直接把原數據發回去。但實際開發中&#xff0c;業務邏輯會復雜得多&#xff08;比如查字…