服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - Google.Protobuf工具在C#中的使用方法

Google.Protobuf工具在C#中的使用方法

2022-12-09 12:49chenzk C#

本文详细讲解了Google.Protobuf工具在C#中的使用方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

protobuf是一个语言无关、平台无关的序列化协议,由谷歌开源提供。再加上其高性能、存储占用更小等特点,在云原生的应用中越来越广泛。

在C#中主要有两种方法来使用protobuf协议,nuget包分别为Google.Protobufprotobuf-net,其中Google.Protobuf由谷歌官方提供。本文简要记录和展示Google.Protobuf的使用方法和特点。

项目资料及文档

  • 项目官网:https://developers.google.cn/protocol-buffers?hl=zh-cn
  • github主页:https://github.com/protocolbuffers/protobuf/
  • 官方文档:https://developers.google.cn/protocol-buffers/docs/overview?hl=zh-cn
  • 该nuget包支持.NETFramework 4.5、.NETStandard1.1、.net5等

准备工作

需要用到的nuget有如下两个:Google.ProtobufGoogle.Protobuf.Tools,其中Google.Protobuf是主类库,运行时要用到。Google.Protobuf.Tools提供了命令行工具,用于根据.proto文件转为目标语言的类型,仅开发时使用,运行时不需要。

本次Demo使用的.proto文件内容如下:

?
1
2
3
4
5
6
7
8
9
10
syntax = "proto3";
option cc_enable_arenas = true;
 
package Tccc.Demo.Protobuf;
 
message ErrorLog {
    string LogID = 1;
    string Context = 2;
    string Stack = 3;
}

首先需要根据.proto文件生成目标类型,操作如下:

?
1
./google.protobuf.tools\3.19.1\tools\windows_x64\protoc.exe --csharp_out=./generatedCode ./proto/ErrorLog.proto

其中--csharp_out选项是生成C#语言的目标类型,运行protoc.exe -h 查看帮助信息,可以看到还支持一下几种选项:

?
1
2
3
4
5
6
7
8
9
10
--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.

运行上述命令,会根据指定的ErrorLog.proto文件生成ErrorLog.cs文件,文件中就是C#类型ErrorLog。生成的代码中会给此类型增加方法void WriteTo(CodedOutputStream output)和只读属性Parser,接下来进行序列化和反序列化的关键。

生成的ErrorLog类的完整代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
// <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

序列化操作

?
1
2
3
4
5
6
7
8
public static byte[] Serialize(ErrorLog log)
        {
            using (MemoryStream output = new MemoryStream())
            {
                log.WriteTo(output);
                return output.ToArray();
            }
        }

反序列化操作

?
1
ErrorLog desErrorLog= ErrorLog.Parser.ParseFrom(data);

使用特点和理解

  • protoc.exe是支持生成多语言类型,这对于跨语言的混合编程比较方便。
  • 根据上述使用步骤可以看到,必须先使用工具protoc生成目标类型,才能调用序列化和反序列化方法,这有些不符合.net平台的编码习惯。
  • 一堆自动生成的C#类在可维护性方面欠佳,当需要调整属性字段时,还要通过工具重新生成,较为麻烦。

到此这篇关于Google.Protobuf工具在C#中的使用方法就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/chen943354/p/15597270.html

延伸 · 阅读

精彩推荐
  • C#C# 中对象序列化XML的方法

    C# 中对象序列化XML的方法

    这篇文章主要介绍了C# 中对象序列化XML的方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...

    王月半子7302022-11-04
  • C#C#七大经典排序算法系列(上)

    C#七大经典排序算法系列(上)

    这篇文章主要为大家详细介绍了C#七大经典排序算法系列上篇,冒泡排序,快速排序等,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    一线码农9152021-12-31
  • C#C#上位机与三菱PLC通讯的实现步骤(图文)

    C#上位机与三菱PLC通讯的实现步骤(图文)

    这篇文章主要介绍了C#上位机与三菱PLC通讯的实现步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的...

    han_better9242022-11-02
  • C#unity 如何获取button文本的内容

    unity 如何获取button文本的内容

    这篇文章主要介绍了unity 获取button文本的内容操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    yguoelect7682022-11-13
  • C#C# 调用腾讯即时通信 IM的示例

    C# 调用腾讯即时通信 IM的示例

    这篇文章主要介绍了C# 调用腾讯即时通信 IM的示例,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...

    正在缓冲10162022-10-17
  • C#C#通过第三方组件生成二维码(QR Code)和条形码(Bar Code)

    C#通过第三方组件生成二维码(QR Code)和条形码(Bar Code)

    用C#如何生成二维码,我们可以通过现有的第三方dll直接来实现,下面列出几种不同的生成方法...

    C#教程网8352021-12-13
  • C#C# 和 Python 的 hash_md5加密方法

    C# 和 Python 的 hash_md5加密方法

    这篇文章主要介绍了C# 和 Python 的 hash_md5加密方法,文章围绕着C# 和 Python 的 hash_md5加密的相关资料展开文章的详细呢偶然,需要的朋友可以参考一下,希望...

    随风去远方5152022-12-08
  • C#C# 构造函数如何调用虚方法

    C# 构造函数如何调用虚方法

    这篇文章主要介绍了C# 构造函数如何调用虚方法,文中讲解非常详细,示例代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    NiKaFace6612022-09-21