Google.Protobuf工具在C#中的使用方法
protobuf是一個(gè)語(yǔ)言無(wú)關(guān)、平臺(tái)無(wú)關(guān)的序列化協(xié)議,由谷歌開(kāi)源提供。再加上其高性能、存儲(chǔ)占用更小等特點(diǎn),在云原生的應(yīng)用中越來(lái)越廣泛。
在C#中主要有兩種方法來(lái)使用protobuf協(xié)議,nuget包分別為Google.Protobuf
和protobuf-net
,其中Google.Protobuf
由谷歌官方提供。本文簡(jiǎn)要記錄和展示Google.Protobuf
的使用方法和特點(diǎn)。
項(xiàng)目資料及文檔
- 項(xiàng)目官網(wǎng):https://developers.google.cn/protocol-buffers?hl=zh-cn
- github主頁(yè):https://github.com/protocolbuffers/protobuf/
- 官方文檔:https://developers.google.cn/protocol-buffers/docs/overview?hl=zh-cn
- 該nuget包支持.NETFramework 4.5、.NETStandard1.1、.net5等
準(zhǔn)備工作
需要用到的nuget有如下兩個(gè):Google.Protobuf
、Google.Protobuf.Tools
,其中Google.Protobuf
是主類(lèi)庫(kù),運(yùn)行時(shí)要用到。Google.Protobuf.Tools
提供了命令行工具,用于根據(jù).proto文件轉(zhuǎn)為目標(biāo)語(yǔ)言的類(lèi)型,僅開(kāi)發(fā)時(shí)使用,運(yùn)行時(shí)不需要。
本次Demo使用的.proto文件內(nèi)容如下:
syntax = "proto3"; option cc_enable_arenas = true; package Tccc.Demo.Protobuf; message ErrorLog { string LogID = 1; string Context = 2; string Stack = 3; }
首先需要根據(jù).proto文件生成目標(biāo)類(lèi)型,操作如下:
./google.protobuf.tools\3.19.1\tools\windows_x64\protoc.exe --csharp_out=./generatedCode ./proto/ErrorLog.proto
其中--csharp_out
選項(xiàng)是生成C#語(yǔ)言的目標(biāo)類(lèi)型,運(yùn)行protoc.exe -h
查看幫助信息,可以看到還支持一下幾種選項(xiàng):
--proto_path=PATH --cpp_out=OUT_DIR Generate C++ header and source. --csharp_out=OUT_DIR Generate C# source file. --java_out=OUT_DIR Generate Java source file. --js_out=OUT_DIR Generate JavaScript source. --kotlin_out=OUT_DIR Generate Kotlin file. --objc_out=OUT_DIR Generate Objective-C header and source. --php_out=OUT_DIR Generate PHP source file. --python_out=OUT_DIR Generate Python source file. --ruby_out=OUT_DIR Generate Ruby source file.
運(yùn)行上述命令,會(huì)根據(jù)指定的ErrorLog.proto
文件生成ErrorLog.cs文件,文件中就是C#類(lèi)型ErrorLog
。生成的代碼中會(huì)給此類(lèi)型增加方法void WriteTo(CodedOutputStream output)
和只讀屬性Parser
,接下來(lái)進(jìn)行序列化和反序列化的關(guān)鍵。
生成的ErrorLog類(lèi)的完整代碼:
// <auto-generated> // Generated by the protocol buffer compiler. DO NOT EDIT! // source: ProtoFiles/ErrorLog.proto // </auto-generated> #pragma warning disable 1591, 0612, 3021 #region Designer generated code using pb = global::Google.Protobuf; using pbc = global::Google.Protobuf.Collections; using pbr = global::Google.Protobuf.Reflection; using scg = global::System.Collections.Generic; namespace Tccc.Demo.Protobuf { /// <summary>Holder for reflection information generated from ProtoFiles/ErrorLog.proto</summary> public static partial class ErrorLogReflection { #region Descriptor /// <summary>File descriptor for ProtoFiles/ErrorLog.proto</summary> public static pbr::FileDescriptor Descriptor { get { return descriptor; } } private static pbr::FileDescriptor descriptor; static ErrorLogReflection() { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( "ChlQcm90b0ZpbGVzL0Vycm9yTG9nLnByb3RvEhJUY2NjLkRlbW8uUHJvdG9i", "dWYiOQoIRXJyb3JMb2cSDQoFTG9nSUQYASABKAkSDwoHQ29udGV4dBgCIAEo", "CRINCgVTdGFjaxgDIAEoCUID+AEBYgZwcm90bzM=")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::Tccc.Demo.Protobuf.ErrorLog), global::Tccc.Demo.Protobuf.ErrorLog.Parser, new[]{ "LogID", "Context", "Stack" }, null, null, null, null) })); } #endregion } #region Messages public sealed partial class ErrorLog : pb::IMessage<ErrorLog> #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage #endif { private static readonly pb::MessageParser<ErrorLog> _parser = new pb::MessageParser<ErrorLog>(() => new ErrorLog()); private pb::UnknownFieldSet _unknownFields; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pb::MessageParser<ErrorLog> Parser { get { return _parser; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { get { return global::Tccc.Demo.Protobuf.ErrorLogReflection.Descriptor.MessageTypes[0]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] pbr::MessageDescriptor pb::IMessage.Descriptor { get { return Descriptor; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public ErrorLog() { OnConstruction(); } partial void OnConstruction(); [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public ErrorLog(ErrorLog other) : this() { logID_ = other.logID_; context_ = other.context_; stack_ = other.stack_; _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public ErrorLog Clone() { return new ErrorLog(this); } /// <summary>Field number for the "LogID" field.</summary> public const int LogIDFieldNumber = 1; private string logID_ = ""; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public string LogID { get { return logID_; } set { logID_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); } } /// <summary>Field number for the "Context" field.</summary> public const int ContextFieldNumber = 2; private string context_ = ""; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public string Context { get { return context_; } set { context_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); } } /// <summary>Field number for the "Stack" field.</summary> public const int StackFieldNumber = 3; private string stack_ = ""; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public string Stack { get { return stack_; } set { stack_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public override bool Equals(object other) { return Equals(other as ErrorLog); } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public bool Equals(ErrorLog other) { if (ReferenceEquals(other, null)) { return false; } if (ReferenceEquals(other, this)) { return true; } if (LogID != other.LogID) return false; if (Context != other.Context) return false; if (Stack != other.Stack) return false; return Equals(_unknownFields, other._unknownFields); } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public override int GetHashCode() { int hash = 1; if (LogID.Length != 0) hash ^= LogID.GetHashCode(); if (Context.Length != 0) hash ^= Context.GetHashCode(); if (Stack.Length != 0) hash ^= Stack.GetHashCode(); if (_unknownFields != null) { hash ^= _unknownFields.GetHashCode(); } return hash; } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public override string ToString() { return pb::JsonFormatter.ToDiagnosticString(this); } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public void WriteTo(pb::CodedOutputStream output) { #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE output.WriteRawMessage(this); #else if (LogID.Length != 0) { output.WriteRawTag(10); output.WriteString(LogID); } if (Context.Length != 0) { output.WriteRawTag(18); output.WriteString(Context); } if (Stack.Length != 0) { output.WriteRawTag(26); output.WriteString(Stack); } if (_unknownFields != null) { _unknownFields.WriteTo(output); } #endif } #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { if (LogID.Length != 0) { output.WriteRawTag(10); output.WriteString(LogID); } if (Context.Length != 0) { output.WriteRawTag(18); output.WriteString(Context); } if (Stack.Length != 0) { output.WriteRawTag(26); output.WriteString(Stack); } if (_unknownFields != null) { _unknownFields.WriteTo(ref output); } } #endif [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public int CalculateSize() { int size = 0; if (LogID.Length != 0) { size += 1 + pb::CodedOutputStream.ComputeStringSize(LogID); } if (Context.Length != 0) { size += 1 + pb::CodedOutputStream.ComputeStringSize(Context); } if (Stack.Length != 0) { size += 1 + pb::CodedOutputStream.ComputeStringSize(Stack); } if (_unknownFields != null) { size += _unknownFields.CalculateSize(); } return size; } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public void MergeFrom(ErrorLog other) { if (other == null) { return; } if (other.LogID.Length != 0) { LogID = other.LogID; } if (other.Context.Length != 0) { Context = other.Context; } if (other.Stack.Length != 0) { Stack = other.Stack; } _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public void MergeFrom(pb::CodedInputStream input) { #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE input.ReadRawMessage(this); #else uint tag; while ((tag = input.ReadTag()) != 0) { switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; case 10: { LogID = input.ReadString(); break; } case 18: { Context = input.ReadString(); break; } case 26: { Stack = input.ReadString(); break; } } } #endif } #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; case 10: { LogID = input.ReadString(); break; } case 18: { Context = input.ReadString(); break; } case 26: { Stack = input.ReadString(); break; } } } } #endif } #endregion } #endregion Designer generated code
序列化操作
public static byte[] Serialize(ErrorLog log) { using (MemoryStream output = new MemoryStream()) { log.WriteTo(output); return output.ToArray(); } }
反序列化操作
ErrorLog desErrorLog= ErrorLog.Parser.ParseFrom(data);
使用特點(diǎn)和理解
- protoc.exe是支持生成多語(yǔ)言類(lèi)型,這對(duì)于跨語(yǔ)言的混合編程比較方便。
- 根據(jù)上述使用步驟可以看到,必須先使用工具protoc生成目標(biāo)類(lèi)型,才能調(diào)用序列化和反序列化方法,這有些不符合.net平臺(tái)的編碼習(xí)慣。
- 一堆自動(dòng)生成的C#類(lèi)在可維護(hù)性方面欠佳,當(dāng)需要調(diào)整屬性字段時(shí),還要通過(guò)工具重新生成,較為麻煩。
到此這篇關(guān)于Google.Protobuf工具在C#中的使用方法就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#使用后臺(tái)線程BackgroundWorker處理任務(wù)的總結(jié)
這篇文章主要介紹了C#使用后臺(tái)線程BackgroundWorker處理任務(wù)的總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07C#將Sql數(shù)據(jù)保存到Excel文件中的方法
這篇文章主要介紹了C#將Sql數(shù)據(jù)保存到Excel文件中的方法,文中的ExportExcel可起到將sql數(shù)據(jù)導(dǎo)出為Excel的作用,需要的朋友可以參考下2014-08-08C#實(shí)現(xiàn)windows系統(tǒng)重啟和關(guān)機(jī)的代碼詳解
這篇文章主要介紹了C#實(shí)現(xiàn)windows系統(tǒng)重啟和關(guān)機(jī)的的方法,涉及C#調(diào)用windows系統(tǒng)命令實(shí)現(xiàn)控制開(kāi)機(jī)、關(guān)機(jī)等操作的技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2024-02-02C# 調(diào)用Delphi dll 實(shí)例代碼
這篇文章介紹了C# 調(diào)用Delphi dll 實(shí)例代碼,有需要的朋友可以參考一下2013-09-09C#?二進(jìn)制序列化和反序列化的具體實(shí)現(xiàn)
本文主要介紹了C#?二進(jìn)制序列化和反序列化的具體實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06unity 文件流讀取圖片與www讀取圖片的區(qū)別介紹
這篇文章主要介紹了unity 文件流讀取圖片與www讀取圖片的對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04