protobuf是一个语言无关、平台无关的序列化协议,由谷歌开源提供。再加上其高性能、存储占用更小等特点,在云原生的应用中越来越广泛。
在C#中主要有两种方法来使用protobuf协议,nuget包分别为Google.Protobuf和protobuf-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.Protobuf、Google.Protobuf.Tools,其中Google.Protobuf是主类库,运行时要用到。Google.Protobuf.Tools提供了命令行工具,用于根据.proto文件转为目标语言的类型,仅开发时使用,运行时不需要。
  本次Demo使用的.proto文件内容如下:
syntax = "proto3";
option cc_enable_arenas = true; package Tccc.Demo.Protobuf; message ErrorLog {
string LogID = 1;
string Context = 2;
string Stack = 3;
}
  首先需要根据.proto文件生成目标类型,操作如下:
./google.protobuf.tools\3.19.1\tools\windows_x64\protoc.exe --csharp_out=./generatedCode ./proto/ErrorLog.proto

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

--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类的完整代码:

// <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);

使用特点和理解

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

如何在C#中使用Google.Protobuf工具的更多相关文章

  1. 在UnrealEngine4中使用Google Protobuf

    转自:https://blog.csdn.net/or_7r_ccl/article/details/54986393 在UnrealEngine4中使用Google Protobuf         ...

  2. 如何在Blog中加入Google Analytics

    原文链接:https://www.cnblogs.com/procoder/archive/2010/03/04/Google-Analytics-Blog.html 背景 在之前加入了一个网站统计工 ...

  3. 如何在sharepoint2010中配置Google Anlytics 分析服务

      简介 Google Analytics(分析)不仅可以帮助您衡量销售与转化情况,而且能为您提供新鲜的深入信息,帮助您了解访问者如何使用您的网站,他们如何到达您的网站,以及您可以如何吸引他们不断回访 ...

  4. 如何在eclipse中配置反编译工具JadClipse

    Q:为什么有必要在开发环境中配置反编译工具呢? A:  当运行引用了第三方jar包项目时,突然报出了jar包中的某个类的某一行出现异常.我们想看一下这个class文件的代码时,经常出现了如下图所示的场 ...

  5. 如何在Linux中使用rz/sz工具进行文件传输

    在Linux中,使用rz/sz工具能够进行Linux和windows之间的文件传输,那么要如何使用rz/sz工具工具呢?下面小编就给大家介绍下Linux下如何使用rz/sz工具进行文件传输,一起来学习 ...

  6. 《Dotnet9》系列-Google ProtoBuf在C#中的简单应用

    时间如流水,只能流去不流回! 点赞再看,养成习惯,这是您给我创作的动力! 本文 Dotnet9 https://dotnet9.com 已收录,站长乐于分享dotnet相关技术,比如Winform.W ...

  7. Netty学习——Google Protobuf使用方式分析和环境搭建

    Google Protobuf使用方式分析 在RPC框架中,Google Protobuf是很常用的一个库,和Apache Thrift 是同款的用于进行序列化的第三方库.原理都是大同小异,无非就是使 ...

  8. Java中使用google.zxing快捷生成二维码(附工具类源码)

    移动互联网时代,基于手机端的各种活动扫码和收付款码层出不穷:那我们如何在Java中生成自己想要的二维码呢?下面就来讲讲在Java开发中使用 google.zxing 生成二维码. 一般情况下,Java ...

  9. (原)python中import caffe提示no module named google.protobuf.internal

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5993405.html 之前在一台台式机上在python中使用import caffe时,没有出错.但是 ...

随机推荐

  1. Java初步学习——2021.09.24每日总结,第三周周五

    (1)今天做了什么: (2)明天准备做什么? (3)遇到的问题,如何解决? 今天学了将数组传递给方法和方法返回数组,其中传递的是数组的引用. 明天把例子做了,尽量把查找也学习了. 遇到了两个问题: 1 ...

  2. 分布式应用开发 | SpringBoot+dubbo+zookeeper实现服务注册发现 | 远程服务调用

    前言 通过新建两个独立服务--提供者.消费者,模拟两个独立分布的应用,通过使用dubbo+zookeeper来实现远程服务调用. 目录 项目搭建 provider-server consumer-se ...

  3. 👊 Spring技术原理系列(7)带你看看那些可能你还不知道的Spring特性技巧哦!

    前提介绍 本文主要介绍相关Spring框架的一些新特性问题机制,包含了一些特定注解方面的认识. @Lazy可以延迟依赖注入 @Lazy注解修饰在类层面! @Lazy @Service public c ...

  4. 树莓派4B学习札记

    防静电 树莓派比较容易被静电损坏,要做好以下预防措施 使用的时候不要用手去触摸PCB和针脚!特别是上电之后! 拿板卡的时候,要习惯性拿板卡的边缘 勤洗手,勤摸墙壁,释放身上的静电 系统安装 8GB以上 ...

  5. dubbo注册中心占位符无法解析问题(二)

    dubbo注册中心占位符无法解析问题 前面分析了dubbo注册中心占位符无法解析的问题. 并给出了2种解决办法: 降低mybatis-spring的版本至2.0.1及以下 自定义MapperScann ...

  6. 初识HTML02

    HTML 超文本标记语言 什么是超文本标记语言 浏览器能够解释和解析的语言 通过元素的形式构建页面结构和填充内容 构建HTML页面 构建页面的步骤 创建一个扩展名为.html和.html的页面文件 向 ...

  7. RabbitMQ:从入门到搞定面试官

    安装 使用docker安装,注意要安装tag后缀为management的镜像(包含web管理插件),我这里使用的是rabbitmq:3.8-management 1. 拉取镜像 shell docke ...

  8. Python爬虫之爬取淘女郎照片示例详解

    这篇文章主要介绍了Python爬虫之爬取淘女郎照片示例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 本篇目标 抓取淘宝MM ...

  9. Exynos4412 中断处理流程详解

    Linux 中,当外设触发中断后,大体处理流程如下: a -- 具体CPU architecture相关的模块会进行现场保护,然后调用machine driver对应的中断处理handler; b - ...

  10. 21.6.17 test

    \(NOI\) 模拟赛. \(T1\) 正解树形DP,由于不是很熟悉概率和期望所以打了个20pts暴力,说不定见多了概率能打出60pts半正解?最后的虚树更不会. \(T2\) 又是概率,还有坐标数量 ...