日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

使用CSharp编写Google Protobuf插件

發(fā)布時(shí)間:2025/7/14 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用CSharp编写Google Protobuf插件 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

什么是 Google Protocol Buffer?

Google Protocol Buffer( 簡(jiǎn)稱 Protobuf) 是 Google 公司內(nèi)部的混合語(yǔ)言數(shù)據(jù)標(biāo)準(zhǔn),目前已經(jīng)正在使用的有超過(guò) 48,162 種報(bào)文格式定義和超過(guò) 12,183 個(gè) .proto 文件。他們用于 RPC 系統(tǒng)和持續(xù)數(shù)據(jù)存儲(chǔ)系統(tǒng)。

Protocol Buffers 是一種輕便高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,可以用于結(jié)構(gòu)化數(shù)據(jù)串行化,或者說(shuō)序列化。它很適合做數(shù)據(jù)存儲(chǔ)或 RPC 數(shù)據(jù)交換格式。可用于通訊協(xié)議、數(shù)據(jù)存儲(chǔ)等領(lǐng)域的語(yǔ)言無(wú)關(guān)、平臺(tái)無(wú)關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)格式。目前提供了 多種語(yǔ)言的API,包括C++、 C# 、GO、 JAVA、 PYTHON

如果你并不了解Protobuf能做什么,建議結(jié)合google搜索關(guān)鍵字,看一下入門(mén)級(jí)別的文章,或者看一下官方文檔中的Developer Guide,或者中文的開(kāi)發(fā)指南 .官方的文檔中有各種語(yǔ)言相關(guān)的示例,可以結(jié)合代碼看一下實(shí)際的用法。

很多人說(shuō)為什么不用json(或者xml), 答案很簡(jiǎn)單,Protobuf更小,更簡(jiǎn)潔,而且序列化和反序列化更快!

谷歌最新開(kāi)源的gRpc框架就是默認(rèn)使用Protobuf作為數(shù)據(jù)傳輸格式和服務(wù)描述文件。對(duì)于gRpc 就不做詳細(xì)介紹了,有興趣的可以看一下官網(wǎng)。

言歸正傳,在實(shí)際使用Protobuf過(guò)程中,我發(fā)現(xiàn)Protobuf不但可以編寫(xiě)描述消息(Message)的內(nèi)容,同時(shí)可以表述其他方法(類(lèi)似Rpc中的方法),主要是gRpc中看到的。同時(shí)在Protobuf 代碼生成工具的包中,有一個(gè)這樣的目錄,一致以來(lái)都沒(méi)搞明白是做什么用的,如下圖:

在目錄中存在大量已經(jīng)定義好的proto文件,其實(shí)這些文件是Protobuf的描述文件,類(lèi)似元數(shù)據(jù)。用本身的語(yǔ)法描述本身,同時(shí)通過(guò)這些文件生成對(duì)應(yīng)的語(yǔ)言的元數(shù)據(jù)類(lèi)等代碼,比如在C#版本的Google.Protobuf中就能看到上述描述文件生成的類(lèi),如下圖所示

而這些描述文件中最重要的文件 就是descriptor.proto 這個(gè)文件,這個(gè)文件是整個(gè)proto語(yǔ)法的描述類(lèi),描述了實(shí)際Protobuf各層次語(yǔ)法的結(jié)構(gòu),來(lái)一起看一下這個(gè)文件的一些代碼, 上面這個(gè)代碼描述了proto文件定義的語(yǔ)法定義,如前面兩個(gè)字段意思是可選的name,可選的package字段,中間是描述可多個(gè)message_type(Message),service(Rpc Service) ,enum_type(枚舉)等定義,然后一層層分解下去。 基本上就可以了解Protobuf語(yǔ)法的全貌和擴(kuò)展點(diǎn)了

message FileDescriptorProto {optional string name = 1; // file name, relative to root of source treeoptional string package = 2; // e.g. "foo", "foo.bar", etc.// Names of files imported by this file.repeated string dependency = 3;// Indexes of the public imported files in the dependency list above.repeated int32 public_dependency = 10;// Indexes of the weak imported files in the dependency list.// For Google-internal migration only. Do not use.repeated int32 weak_dependency = 11;// All top-level definitions in this file.repeated DescriptorProto message_type = 4;repeated EnumDescriptorProto enum_type = 5;repeated ServiceDescriptorProto service = 6;repeated FieldDescriptorProto extension = 7;optional FileOptions options = 8;// This field contains optional information about the original source code.// You may safely remove this entire field without harming runtime// functionality of the descriptors -- the information is needed only by// development tools.optional SourceCodeInfo source_code_info = 9;// The syntax of the proto file.// The supported values are "proto2" and "proto3".optional string syntax = 12; }

同時(shí)在compiler目錄下 還有一個(gè)plugin的目錄,其中的plugin.proto文件很耐人尋味,先來(lái)看下這個(gè)文件中的內(nèi)容

syntax = "proto3"; package google.protobuf.compiler; option java_package = "com.google.protobuf.compiler"; option java_outer_classname = "PluginProtos";option csharp_namespace = "Google.Protobuf.Compiler";option go_package = "plugin_go";import "google/protobuf/descriptor.proto";message CodeGeneratorRequest {repeated string file_to_generate = 1;string parameter = 2;repeated FileDescriptorProto proto_file = 15; }message CodeGeneratorResponse { string error = 1; message File { string name = 1;string insertion_point = 2;string content = 15;}repeated File file = 15; }

刪除了非必要的注釋后,我們可以看到這個(gè)文件里面其實(shí)只定義了兩個(gè)類(lèi)型,一個(gè)是代碼生成請(qǐng)求,一個(gè)是代碼生成響應(yīng),而在CodeGeneratorRequest中又有之前我們?cè)赿escriptor.proto中看到的FileDescriptorProto 這個(gè)類(lèi)的信息,用大腿都可以想到這里應(yīng)該就是代碼生成插件獲取元數(shù)據(jù)的入口了,那么怎么做呢?

從gRpc 的代碼生成示例中 我們可以看到 其實(shí)Protobuf是支持自定義生成代碼插件的,如下所示:

%PROTOC% -I../../protos --csharp_out Greeter ../../protos/helloworld.proto --grpc_out Greeter --plugin=protoc-gen-grpc=%PLUGIN%

按理我們可以實(shí)現(xiàn)自己的插件來(lái)生成我們需要的任意格式,包括各種代碼,甚至是文檔。但是這個(gè)資料卻非常少,幾乎沒(méi)有多少相關(guān)的文章,后來(lái)終于找到一片關(guān)于plugin的文章http://www.expobrain.net/2015/09/13/create-a-plugin-for-google-protocol-buffer/ ,大家有興趣的可以看看,不過(guò)文章的重點(diǎn)是這句:

The core part is the interface code to read a request from the stdin, traverse the AST and write the response on the stdout.

原來(lái)插件的接口代碼其實(shí)是從標(biāo)準(zhǔn)輸入中讀取流,然后再把你要生成的內(nèi)容輸出到標(biāo)準(zhǔn)輸出中。這些終于知道怎么用了。。

撩起袖子開(kāi)始干,通過(guò)protoc命令行生成plugin.proto的代碼

protoc-I../../protos --csharp_out test ../../protos/plugin.proto

新建一個(gè)控制臺(tái)項(xiàng)目,把代碼copy 到項(xiàng)目中,并在Program.cs代碼中添加測(cè)試的代碼

using Google.Protobuf; using Google.Protobuf.Compiler; using System;namespace DotBPE.ProtobufPlugin {class Program{static void Main(string[] args){Console.OutputEncoding = System.Text.Encoding.UTF8;var response = new CodeGeneratorResponse();try{CodeGeneratorRequest request;using (var inStream = Console.OpenStandardInput()){request = CodeGeneratorRequest.Parser.ParseFrom(inStream);}ParseCode(request, response);}catch (Exception e){response.Error += e.ToString();}using (var output = Console.OpenStandardOutput()){response.WriteTo(output);output.Flush();}}private static void ParseCode(CodeGeneratorRequest request, CodeGeneratorResponse response){DotbpeGen.Generate(request,response);}} }

哈哈 開(kāi)始編譯,然而編譯不通過(guò)!,坑爹啊! 原來(lái)C#版本中 Google.Protobuf已經(jīng)生成好的類(lèi) 都是internal訪問(wèn)權(quán)限,不能從外部引用。。。但是Google.Protobuf是開(kāi)源的。。而且我需要用的類(lèi) 我也可以通過(guò)protoc命令自己生成到同一個(gè)項(xiàng)目中,或者設(shè)置成public訪問(wèn)權(quán)限。。方便起見(jiàn),我直接copy了Google.Protobuf的源碼到我們的項(xiàng)目中,這次再次編譯 ,代碼就完美運(yùn)行了,接下來(lái)的工作 不過(guò)是填充DotbpeGen.Generate 的代碼了,這不過(guò)是體力活。

至于CodeGeneratorRequest和CodeGeneratorResponse 到底有什么方法,其實(shí)看proto文件就能知道。以下是我自己在項(xiàng)目中使用的生成代碼類(lèi) 供大家參考

using Google.Protobuf.Compiler; using Google.Protobuf.Reflection; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text;namespace DotBPE.ProtobufPlugin {public class DotbpeGen{public static void Generate(CodeGeneratorRequest request, CodeGeneratorResponse response){foreach (var protofile in request.ProtoFile){try{GenerateByProtoFile(protofile, response);}catch(Exception ex){using (Stream stream = File.Create("./error.txt")){byte[] err = Encoding.UTF8.GetBytes(ex.Message+ex.StackTrace);stream.Write(err,0,err.Length);}response.Error += ex.Message;}}}private static void GenerateSourceInfo(FileDescriptorProto protofile, CodeGeneratorResponse response){bool genericDoc;protofile.Options.CustomOptions.TryGetBool(DotBPEOptions.GENERIC_MARKDOWN_DOC,out genericDoc);if (!genericDoc){return;}StringBuilder sb = new StringBuilder();foreach (var location in protofile.SourceCodeInfo.Location){string path = String.Join(",", location.Path);string span = String.Join(",", location.Span);string leadingDetachedComments = String.Join("\r", location.LeadingDetachedComments);string trailingComments = String.Join("\r", location.TrailingComments);sb.AppendLine("{\"Path\",\""+path+"\",\"Span\",\""+span+"\",\"LeadingComments\",\""+ location.LeadingComments + "\",\"LeadingDetachedComments\",\""+ leadingDetachedComments + "\",\"TrailingComments\",\""+ trailingComments + "\"}");}var nfile = new CodeGeneratorResponse.Types.File{Name = GetFileName(protofile.Name) + "SI.txt",Content = sb.ToString()};response.File.Add(nfile);}private static void GenerateByProtoFile(FileDescriptorProto protofile, CodeGeneratorResponse response){GenerateSourceInfo(protofile, response);GenerateServer(protofile, response);GenerateClient(protofile, response);}private static void GenerateServer(FileDescriptorProto protofile, CodeGeneratorResponse response){bool genericServer;protofile.Options.CustomOptions.TryGetBool(DotBPEOptions.DISABLE_GENERIC_SERVICES_SERVER, out genericServer);if (genericServer){return;}if (protofile.Service == null || protofile.Service.Count <= 0) return;//生成文件頭StringBuilder sb = new StringBuilder();sb.AppendLine("// Generated by the protocol buffer compiler. DO NOT EDIT!");sb.AppendLine($"// source: {protofile.Name}");//還可以生成注釋sb.AppendLine("#region Designer generated code");sb.AppendLine("");sb.AppendLine("using System; ");sb.AppendLine("using System.Threading.Tasks; ");sb.AppendLine("using DotBPE.Rpc; ");sb.AppendLine("using DotBPE.Protocol.Amp; ");sb.AppendLine("using Google.Protobuf; ");sb.AppendLine("");string ns = GetFileNamespace(protofile);sb.AppendLine("namespace " + ns + " {");//生成代碼foreach (ServiceDescriptorProto t in protofile.Service){t.Options.CustomOptions.TryGetBool(DotBPEOptions.DISABLE_GENERIC_SERVICES_SERVER, out genericServer);if (genericServer){continue;}sb.AppendLine("");sb.AppendLine("//start for class Abstract"+t.Name);GenerateServiceForServer(t, sb);sb.AppendLine("//end for class Abstract"+t.Name);}sb.AppendLine("}\n");sb.AppendLine("#endregion\n");var nfile = new CodeGeneratorResponse.Types.File{Name = GetFileName(protofile.Name) + "Server.cs",Content = sb.ToString()};response.File.Add(nfile);}private static void GenerateClient(FileDescriptorProto protofile, CodeGeneratorResponse response){bool genericClient;protofile.Options.CustomOptions.TryGetBool(DotBPEOptions.DISABLE_GENERIC_SERVICES_CLIENT, out genericClient);if (genericClient){return;}if (protofile.Service == null || protofile.Service.Count <= 0) return;//生成文件頭StringBuilder sb = new StringBuilder();sb.AppendLine("// Generated by the protocol buffer compiler. DO NOT EDIT!");sb.AppendLine($"// source: {protofile.Name}");//還可以生成注釋sb.AppendLine("#region Designer generated code");sb.AppendLine("");sb.AppendLine("using System; ");sb.AppendLine("using System.Threading.Tasks; ");sb.AppendLine("using DotBPE.Rpc; ");sb.AppendLine("using DotBPE.Protocol.Amp; ");sb.AppendLine("using DotBPE.Rpc.Exceptions; ");sb.AppendLine("using Google.Protobuf; ");sb.AppendLine("");string ns = GetFileNamespace(protofile);sb.AppendLine("namespace " + ns + " {");//生成代碼foreach (ServiceDescriptorProto t in protofile.Service){t.Options.CustomOptions.TryGetBool(DotBPEOptions.DISABLE_GENERIC_SERVICES_CLIENT, out genericClient);if (genericClient){continue;}sb.AppendLine("");sb.AppendLine("//start for class "+t.Name+"Client");GenerateServiceForClient(t, sb);sb.AppendLine("//end for class "+t.Name+"Client");}sb.AppendLine("}");sb.AppendLine("#endregion");//生成文件var nfile = new CodeGeneratorResponse.Types.File{Name = GetFileName(protofile.Name) + "Client.cs",Content = sb.ToString()};response.File.Add(nfile);}private static void GenerateServiceForClient(ServiceDescriptorProto service, StringBuilder sb){int serviceId;bool hasServiceId = service.Options.CustomOptions.TryGetInt32(DotBPEOptions.SERVICE_ID, out serviceId);if (!hasServiceId || serviceId <= 0){throw new Exception("Service=" + service.Name + " ServiceId NOT_FOUND");}if (serviceId >= ushort.MaxValue){throw new Exception("Service=" + service.Name + "ServiceId too large");}sb.AppendFormat("public sealed class {0}Client : AmpInvokeClient \n",service.Name);sb.AppendLine("{");//構(gòu)造函數(shù)sb.AppendLine($"public {service.Name}Client(IRpcClient<AmpMessage> client) : base(client)");sb.AppendLine("{");sb.AppendLine("}");//循環(huán)方法foreach (var method in service.Method){int msgId ;bool hasMsgId= method.Options.CustomOptions.TryGetInt32(DotBPEOptions.MESSAGE_ID,out msgId);if (!hasMsgId || msgId <= 0){throw new Exception("Service" + service.Name + "." + method.Name + " ' MessageId NOT_FINDOUT ");}if (msgId >= ushort.MaxValue){throw new Exception("Service" + service.Name + "." + method.Name + " is too large");}//異步方法string outType = GetTypeName(method.OutputType);string inType = GetTypeName(method.InputType);sb.AppendLine($"public async Task<{outType}> {method.Name}Asnyc({inType} request,int timeOut=3000)");sb.AppendLine("{");sb.AppendLine($"AmpMessage message = AmpMessage.CreateRequestMessage({serviceId}, {msgId});");sb.AppendLine("message.Data = request.ToByteArray();");sb.AppendLine("var response = await base.CallInvoker.AsyncCall(message,timeOut);");sb.AppendLine("if (response != null && response.Data !=null)");sb.AppendLine("{");sb.AppendLine($"return {outType}.Parser.ParseFrom(response.Data);");sb.AppendLine("}");sb.AppendLine("throw new RpcException(\"請(qǐng)求出錯(cuò),請(qǐng)檢查!\");");sb.AppendLine("}");sb.AppendLine();sb.AppendLine("//同步方法");sb.AppendLine($"public {outType} {method.Name}({inType} request)");sb.AppendLine("{");sb.AppendLine($"AmpMessage message = AmpMessage.CreateRequestMessage({serviceId}, {msgId});");sb.AppendLine("message.Data = request.ToByteArray();");sb.AppendLine("var response = base.CallInvoker.BlockingCall(message);");sb.AppendLine("if (response != null && response.Data !=null)");sb.AppendLine("{");sb.AppendLine($"return {outType}.Parser.ParseFrom(response.Data);");sb.AppendLine("}");sb.AppendLine("throw new RpcException(\"請(qǐng)求出錯(cuò),請(qǐng)檢查!\");");sb.AppendLine("}");}//循環(huán)方法endsb.AppendLine("}");//類(lèi)結(jié)束}private static void GenerateServiceForServer(ServiceDescriptorProto service, StringBuilder sb){int serviceId;bool hasServiceId = service.Options.CustomOptions.TryGetInt32(DotBPEOptions.SERVICE_ID, out serviceId);if(!hasServiceId || serviceId<=0){throw new Exception("Service="+service.Name+" ServiceId NOT_FOUND");}if(serviceId>=ushort.MaxValue){throw new Exception("Service="+service.Name+ "ServiceId too large" );}sb.AppendFormat("public abstract class {0}Base : IServiceActor<AmpMessage> \n", service.Name);sb.AppendLine("{");sb.AppendLine("public string Id => \""+serviceId+"$0\";");StringBuilder sbIfState = new StringBuilder();//循環(huán)方法foreach (var method in service.Method){int msgId ;bool hasMsgId= method.Options.CustomOptions.TryGetInt32(DotBPEOptions.MESSAGE_ID,out msgId);if(!hasMsgId || msgId<=0){throw new Exception("Service"+service.Name+"."+method.Name+" ' MessageId NOT_FINDOUT ");}if(msgId>=ushort.MaxValue){throw new Exception("Service" + service.Name+"."+method.Name+" is too large");}//異步方法string outType = GetTypeName(method.OutputType);string inType = GetTypeName(method.InputType);sb.AppendLine("//調(diào)用委托");sb.AppendLine($"private async Task Receive{method.Name}Async(IRpcContext<AmpMessage> context, AmpMessage req)");sb.AppendLine("{");sb.AppendLine($"var request = {inType}.Parser.ParseFrom(req.Data);");sb.AppendLine($"var data = await {method.Name}Async(request);");sb.AppendLine("var response = AmpMessage.CreateResponseMessage(req.ServiceId, req.MessageId);");sb.AppendLine("response.Sequence = req.Sequence;");sb.AppendLine("response.Data = data.ToByteArray();");sb.AppendLine("await context.SendAsync(response);");sb.AppendLine("}");sb.AppendLine();sb.AppendLine("//抽象方法");sb.AppendLine($"public abstract Task<{outType}> {method.Name}Async({inType} request);");//拼裝if調(diào)用語(yǔ)句sbIfState.AppendFormat("//方法{0}.{1}\n",service.Name,method.Name);sbIfState.AppendLine("if(req.MessageId == "+msgId+"){return this.Receive"+method.Name+"Async(context, req);}");}//循環(huán)方法end//生成主調(diào)用代碼sb.AppendLine("public Task ReceiveAsync(IRpcContext<AmpMessage> context, AmpMessage req)");sb.AppendLine("{");sb.Append(sbIfState);sb.AppendLine("return Task.CompletedTask;");sb.AppendLine("}");sb.AppendLine("}");//類(lèi)結(jié)束}private static string GetFileNamespace(FileDescriptorProto protofile){string ns = protofile.Options.CsharpNamespace;if (string.IsNullOrEmpty(ns)){throw new Exception("" + protofile.Name + ".proto did not set csharp_namespace");}return ConvertCamelCase(ns);}private static string GetFileName(string fileProto){string nomalName = fileProto.Split('.')[0];return ConvertCamelCase(nomalName);}private static string ConvertCamelCase(string nomalName){return String.Join("", nomalName.Split('_').Select(_ => _.Substring(0, 1).ToUpper() + _.Substring(1)));}private static string GetTypeName(string typeFullName){return ConvertCamelCase(typeFullName.Split('.').Last());}} }

然后我們編寫(xiě)一個(gè)proto文件測(cè)試以下

//benchmark.protosyntax = "proto3"; package dotbpe;option csharp_namespace = "DotBPE.IntegrationTesting";import public "dotbpe_option.proto";option optimize_for = SPEED;//Benchmark測(cè)試服務(wù) service BenchmarkTest{option (service_id)= 50000 ;//設(shè)定服務(wù)ID//測(cè)試發(fā)送Echo消息rpc Echo (BenchmarkMessage) returns (BenchmarkMessage){option (message_id)= 1 ;//設(shè)定消息ID};//Echo尾部的注釋// 測(cè)試發(fā)送退出消息rpc Quit (Void) returns (Void){option (message_id)= 10000 ;//設(shè)定消息ID};//Quit尾部的注釋 }//我是void消息 message Void {} //我是BenchmarkMessage消息 message BenchmarkMessage {//字段前的注釋string field1 = 1; //字段后的注釋//字段前的注釋 多行//字段前的字?jǐn)?shù)多行int32 field2 = 2; //字段后的注釋/*** 字段前注釋特殊格式* 字段前注釋特殊格式多行*/int32 field3 = 3;string field4 = 4;repeated fixed64 field5 = 5;string field9 = 9;string field18 = 18;bool field80 = 80;bool field81 = 81;int32 field280 = 280 ;int32 field6 = 6;int64 field22 = 22 ;bool field59 = 59 ;string field7 = 7;int32 field16 = 16 ;int32 field130 = 130 ;bool field12 = 12 ;bool field17 = 17;bool field13 = 13;bool field14 = 14;int32 field104 = 104 ;int32 field100 = 100 ;int32 field101 = 101 ;string field102 = 102;string field103 = 103;int32 field29 = 29 ;bool field30 = 30 ;int32 field60 = 60 ;int32 field271 = 271 ;int32 field272 = 272;int32 field150 = 150;int32 field23 = 23;bool field24 = 24;int32 field25 = 25 ;bool field78 = 78 ;int32 field67 = 67;int32 field68 = 68 ;int32 field128 = 128 ;string field129 = 129 ;int32 field131 = 131 ; } // dotbpe_option.proto// [START declaration] syntax = "proto3"; package dotbpe; // [END declaration]// [START csharp_declaration] option csharp_namespace = "DotBPE.ProtoBuf"; // [END csharp_declaration]import "google/protobuf/descriptor.proto";//擴(kuò)展服務(wù) extend google.protobuf.ServiceOptions {int32 service_id = 51001;bool disable_generic_service_client = 51003; //是否生成客戶端代碼bool disable_generic_service_server = 51004; //是否生成服務(wù)端代碼 } extend google.protobuf.MethodOptions {int32 message_id = 51002; }extend google.protobuf.FileOptions {bool disable_generic_services_client = 51003; //是否生成客戶端代碼bool disable_generic_services_server = 51004; //是否生成服務(wù)端代碼bool generic_markdown_doc = 51005; //是否生成文檔 }

上面的dotbpe_option.proto 我們proto文件進(jìn)行了自定義的擴(kuò)展,添加一些自己需要的額外信息,其實(shí)所有擴(kuò)展都是對(duì)descriptor.proto中消息的擴(kuò)展。

然后我們通過(guò)命令來(lái)生成一下,這里有個(gè)特殊的約定,一定要注意當(dāng)我們?cè)O(shè)置

protoc-gen-dotbpe=../../tool/ampplugin/dotbpe_amp.exe 插件的名稱protoc-gen-dotbpe時(shí),那么輸出的目錄一定要寫(xiě)成--dotbpe_out ,兩個(gè)名字一點(diǎn)要匹配哦

set -excd $(dirname $0)/../../test/IntegrationTesting/PROTOC=protoc PLUGIN=protoc-gen-dotbpe=../../tool/ampplugin/dotbpe_amp.exe IntegrationTesting_DIR=./DotBPE.IntegrationTesting/$PROTOC -I=./protos --csharp_out=$IntegrationTesting_DIR --dotbpe_out=$IntegrationTesting_DIR \./protos/benchmark.proto --plugin=$PLUGIN

差不多就結(jié)束了,相關(guān)的代碼可以在https://github.com/xuanye/dotbpe/tree/develop/src/tool 查看到,這是我最近在寫(xiě)的一個(gè)C#的rpc框架,現(xiàn)在完成了基本的功能,還需要進(jìn)一步完善,有機(jī)會(huì)再介紹把。

descriptor.proto信息挖掘

我們注意到在descriptor.proto文件中包含有這樣的一個(gè)message: SourceCodeInfo, 這個(gè)消息體里有如下字段

optional string leading_comments = 3;optional string trailing_comments = 4;repeated string leading_detached_comments = 6;

這是非常有意思的定義,意思是可以在運(yùn)行時(shí)獲取到proto文件中的注釋。這可以幫助我們生成 文檔或者代碼注釋,但是讀取邏輯比較復(fù)雜,其內(nèi)部有一個(gè)通過(guò)Path和Span來(lái)定位元素的邏輯。因?yàn)樵趯?shí)際的情況中,一般都是要獲取Service和Message上的注釋,那么就來(lái)專(zhuān)門(mén)討論一下如何獲取這兩個(gè)類(lèi)型的注釋吧。

下面是 SourceCodeInfo.Location 中我們需要用到Path示例

* [4, m] - Message的注釋* [4, m, 2, f] - Message 中 字段(field)的注釋* [6, s] - Service的注釋* [6, s, 2, r] - Service中Rpc方法的注釋

where:

  • m - proto文件中Message的索引(就是第幾個(gè)定義的Message), 從0開(kāi)始
  • f - Message中Field字段的索引(就是第幾個(gè)字段), 從0開(kāi)始
  • s - proto文件中Service的索引, 從0開(kāi)始
  • r - Service中Rpc方法的索引, 從0開(kāi)始

like this:

// [4, 0] 就是這里的注釋 message MyMessage {// [4, 0, 2, 0] 在這里int32 field1 = 1; // [4, 0, 2, 0] 也在這里 }// [4, 0] 就是這里的注釋 // [6, 0] 在這里! service MyService {// [6, 0, 2, 0] 在這里!rpc (MyMessage) returns (MyMessage); }

想要了解全部?jī)?nèi)容可以去看下descriptor.proto中的注釋內(nèi)容 吧

轉(zhuǎn)載于:https://www.cnblogs.com/xuanye/p/6752872.html

總結(jié)

以上是生活随笔為你收集整理的使用CSharp编写Google Protobuf插件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。