SoFunction
Updated on 2025-03-06

How to use tools in C#

protobuf is a language-independent, platform-independent serialization protocol provided by Google Open Source. In addition, its high performance and smaller storage usage are becoming more and more extensive in cloud-native applications.

There are two main methods to use the protobuf protocol in C#. The nuget packages areandprotobuf-net,inOfficially provided by Google. A brief record and display of this articleHow to use and features.

Project information and documentation

  • Project official website: /protocol-buffers?hl=zh-cn
  • github homepage: /protocolbuffers/protobuf/
  • Official document: /protocol-buffers/docs/overview?hl=zh-cn
  • This nuget package supports .NETFramework 4.5, .NETStandard1.1, .net5, etc.

Preparation

There are two nugets to use:,inIt is the main class library and must be used during runtime.A command line tool is provided for converting to the target language type according to the .proto file, which is only used during development and not required during runtime.

The content of the .proto file used in this demo is as follows:

syntax = "proto3";
option cc_enable_arenas = true;

package ;

message ErrorLog {
    string LogID = 1;
    string Context = 2;
    string Stack = 3;
}

First, you need to generate the target type based on the .proto file, and the operation is as follows:

./\3.19.1\tools\windows_x64\ --csharp_out=./generatedCode ./proto/

in--csharp_outThe option is the target type that generates C# language, run -hCheck the help information and you can see that the following options are also supported:

--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.

Run the above command and the specifiedFile generates file, the file is C# typeErrorLog. The generated code will add methods to this typevoid WriteTo(CodedOutputStream output)and read-only propertiesParser, next to serialization and deserialization key.

The complete code of the generated ErrorLog class:

// <auto-generated>
//     Generated by the protocol buffer compiler.  DO NOT EDIT!
//     source: ProtoFiles/
// </auto-generated>
#pragma warning disable 1591, 0612, 3021
#region Designer generated code

using pb = global::;
using pbc = global::;
using pbr = global::;
using scg = global::;
namespace  {

  /// <summary>Holder for reflection information generated from ProtoFiles/</summary>
  public static partial class ErrorLogReflection {

    #region Descriptor
    /// <summary>File descriptor for ProtoFiles/</summary>
    public static pbr::FileDescriptor Descriptor {
      get { return descriptor; }
    }
    private static pbr::FileDescriptor descriptor;

    static ErrorLogReflection() {
      byte[] descriptorData = global::.FromBase64String(
          (
            "ChlQcm90b0ZpbGVzL0Vycm9yTG9nLnByb3RvEhJUY2NjLkRlbW8uUHJvdG9i",
            "dWYiOQoIRXJyb3JMb2cSDQoFTG9nSUQYASABKAkSDwoHQ29udGV4dBgCIAEo",
            "CRINCgVTdGFjaxgDIAEoCUID+AEBYgZwcm90bzM="));
      descriptor = pbr::(descriptorData,
          new pbr::FileDescriptor[] { },
          new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
            new pbr::GeneratedClrTypeInfo(typeof(global::), global::, 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::]
    [global::("protoc", null)]
    public static pb::MessageParser<ErrorLog> Parser { get { return _parser; } }

    [global::]
    [global::("protoc", null)]
    public static pbr::MessageDescriptor Descriptor {
      get { return global::[0]; }
    }

    [global::]
    [global::("protoc", null)]
    pbr::MessageDescriptor pb:: {
      get { return Descriptor; }
    }

    [global::]
    [global::("protoc", null)]
    public ErrorLog() {
      OnConstruction();
    }

    partial void OnConstruction();

    [global::]
    [global::("protoc", null)]
    public ErrorLog(ErrorLog other) : this() {
      logID_ = other.logID_;
      context_ = other.context_;
      stack_ = other.stack_;
      _unknownFields = pb::(other._unknownFields);
    }

    [global::]
    [global::("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::]
    [global::("protoc", null)]
    public string LogID {
      get { return logID_; }
      set {
        logID_ = pb::(value, "value");
      }
    }

    /// <summary>Field number for the "Context" field.</summary>
    public const int ContextFieldNumber = 2;
    private string context_ = "";
    [global::]
    [global::("protoc", null)]
    public string Context {
      get { return context_; }
      set {
        context_ = pb::(value, "value");
      }
    }

    /// <summary>Field number for the "Stack" field.</summary>
    public const int StackFieldNumber = 3;
    private string stack_ = "";
    [global::]
    [global::("protoc", null)]
    public string Stack {
      get { return stack_; }
      set {
        stack_ = pb::(value, "value");
      }
    }

    [global::]
    [global::("protoc", null)]
    public override bool Equals(object other) {
      return Equals(other as ErrorLog);
    }

    [global::]
    [global::("protoc", null)]
    public bool Equals(ErrorLog other) {
      if (ReferenceEquals(other, null)) {
        return false;
      }
      if (ReferenceEquals(other, this)) {
        return true;
      }
      if (LogID != ) return false;
      if (Context != ) return false;
      if (Stack != ) return false;
      return Equals(_unknownFields, other._unknownFields);
    }

    [global::]
    [global::("protoc", null)]
    public override int GetHashCode() {
      int hash = 1;
      if ( != 0) hash ^= ();
      if ( != 0) hash ^= ();
      if ( != 0) hash ^= ();
      if (_unknownFields != null) {
        hash ^= _unknownFields.GetHashCode();
      }
      return hash;
    }

    [global::]
    [global::("protoc", null)]
    public override string ToString() {
      return pb::(this);
    }

    [global::]
    [global::("protoc", null)]
    public void WriteTo(pb::CodedOutputStream output) {
    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
      (this);
    #else
      if ( != 0) {
        (10);
        (LogID);
      }
      if ( != 0) {
        (18);
        (Context);
      }
      if ( != 0) {
        (26);
        (Stack);
      }
      if (_unknownFields != null) {
        _unknownFields.WriteTo(output);
      }
    #endif
    }

    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
    [global::]
    [global::("protoc", null)]
    void pb::(ref pb::WriteContext output) {
      if ( != 0) {
        (10);
        (LogID);
      }
      if ( != 0) {
        (18);
        (Context);
      }
      if ( != 0) {
        (26);
        (Stack);
      }
      if (_unknownFields != null) {
        _unknownFields.WriteTo(ref output);
      }
    }
    #endif

    [global::]
    [global::("protoc", null)]
    public int CalculateSize() {
      int size = 0;
      if ( != 0) {
        size += 1 + pb::(LogID);
      }
      if ( != 0) {
        size += 1 + pb::(Context);
      }
      if ( != 0) {
        size += 1 + pb::(Stack);
      }
      if (_unknownFields != null) {
        size += _unknownFields.CalculateSize();
      }
      return size;
    }

    [global::]
    [global::("protoc", null)]
    public void MergeFrom(ErrorLog other) {
      if (other == null) {
        return;
      }
      if ( != 0) {
        LogID = ;
      }
      if ( != 0) {
        Context = ;
      }
      if ( != 0) {
        Stack = ;
      }
      _unknownFields = pb::(_unknownFields, other._unknownFields);
    }

    [global::]
    [global::("protoc", null)]
    public void MergeFrom(pb::CodedInputStream input) {
    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
      (this);
    #else
      uint tag;
      while ((tag = ()) != 0) {
        switch(tag) {
          default:
            _unknownFields = pb::(_unknownFields, input);
            break;
          case 10: {
            LogID = ();
            break;
          }
          case 18: {
            Context = ();
            break;
          }
          case 26: {
            Stack = ();
            break;
          }
        }
      }
    #endif
    }

    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
    [global::]
    [global::("protoc", null)]
    void pb::(ref pb::ParseContext input) {
      uint tag;
      while ((tag = ()) != 0) {
        switch(tag) {
          default:
            _unknownFields = pb::(_unknownFields, ref input);
            break;
          case 10: {
            LogID = ();
            break;
          }
          case 18: {
            Context = ();
            break;
          }
          case 26: {
            Stack = ();
            break;
          }
        }
      }
    }
    #endif

  }

  #endregion

}

#endregion Designer generated code

Serialization operations

public static byte[] Serialize(ErrorLog log)
        {
            using (MemoryStream output = new MemoryStream())
            {
                (output);
                return ();
            }
        }

Deserialization operation

ErrorLog desErrorLog= (data);

Usage features and understanding

  • It supports generation of multilingual types, which is more convenient for cross-language hybrid programming.
  • According to the above usage steps, we can see that the target type must be generated by using the tool protoc to call the serialization and deserialization methods, which is somewhat inconsistent with the encoding habits of the .net platform.
  • A bunch of automatically generated C# classes are not good in maintainability. When it is necessary to adjust the attribute fields, it is also regenerated through the tool, which is more troublesome.

This is all about how to use tools in C#. I hope it will be helpful to everyone's learning and I hope everyone will support me more.