【问题标题】:How to read EnumValueOptions from Protobuf in C#?如何在 C# 中从 Protobuf 读取 EnumValueOptions?
【发布时间】:2020-10-14 12:52:22
【问题描述】:

我想读取“EnumValueOptions”类型的字段。我发现的所有示例都与“C#”以外的语言有关。

有一个协议:

syntax = "proto3";   
import "google/protobuf/descriptor.proto"; 

extend google.protobuf.EnumValueOptions {
   string enum_name = 51234;
};;

message Options {
  enum Profiles{
        A = 0 [(enum_name) = "AAA"];
        B = 1 [(enum_name) = "BBB"]; 
        C = 2 [(enum_name) = "CCC"];
  };
  Profiles profile = 1;
}

如何在 C# 代码中读取“enum_name”?

期待:

foreach(Profiles profile in Enum.GetValues(typeof(Profiles)))
{
    string name = ????; //name in ("AAA","BBB","CCC")
}

【问题讨论】:

    标签: c# protocol-buffers


    【解决方案1】:

    Protobuf 不会为枚举值选项生成 c# 代码,因此如果您想检索这些值,您需要在 src/google/protobuf/compiler/csharp/csharp_doc_comment.cc 处编辑源代码。

    这是一个例子

    void WriteEnumValueDocComment(io::Printer* printer, const EnumValueDescriptor* value) 
    {
        WriteDocCommentBody(printer, value);
        
        // Example code to prepend c# attribute like '[Custom(name: value)]' before a enum field
        const Message& options = value->options();
        const Reflection* reflection = options.GetReflection();
        std::vector<const FieldDescriptor*> fields;
        reflection->ListFields(options, &fields);
        for (int i = 0; i < fields.size(); i++)
        {
            std::string name = fields[i]->full_name();
            std::string fieldval;
            TextFormat::PrintFieldValueToString(options, fields[i], -1, &fieldval);
            printer->Print("[Custom($name$: $value$)]", "name", name, "value", fieldval);
        }
    }
    

    重新生成cs文件,然后你可以使用GetCustomAttribute方法来检索这些信息。

    【讨论】:

    • 非常抱歉,没有针对 c# 的实现。
    • 在哪里调用这个方法?
    • 由protoc调用。 @Gardes
    • 错误(活动)E0515:无法转换为不完整的类“const google :: protobuf :: Message” //const Message& options = value->options();
    【解决方案2】:

    原型:

    syntax = "proto2";
    
    package my.protos;
    option csharp_namespace = "My.Protos";
    
    import "google/protobuf/descriptor.proto"; // For enum extension.
    
    extend google.protobuf.EnumValueOptions {
        optional string NameDisplay = 50001;
        optional string NameAbbrev  = 50002;
    }
    
    enum MyOperation {
        MY_CREATE = 0 [(NameDisplay) = "create"];
        MY_DELETE = 1 [(NameDisplay) = "delete"];
        MY_READ   = 2 [(NameDisplay) = "read"];
    }
    

    在 C# 中的用法:

    Google.Protobuf.Reflection.EnumDescriptor descriptor = 
        MyProtosReflection.Descriptor.FindTypeByName<Google.Protobuf.Reflection.EnumDescriptor>(
            typeof(MyOperation).Name
        );
    
    Google.Protobuf.Reflection.EnumValueDescriptor valueDescriptor =
        descriptor.FindValueByNumber((int)MyOperation.Create);
    
    string nameDisplay = valueDescriptor.GetOption(MyProtosExtensions.NameDisplay);
    

    【讨论】:

    • 我相信这是对这个问题的回答。
    • 从哪里获得 MyProtosReflection 和 MyProtosExtensions?
    【解决方案3】:

    我正在用 C# 编程 - 对这个 protobuf 东西还是很陌生

    如果您有以下扩展 EnumValueOptions 的 proto 文件:

    extend google.protobuf.EnumValueOptions {
      string Value = 101;
      bool AutoEnrol = 102;
    }
    

    Protobuf 将通过自动生成生成一个 EnumValueOptionsExtensions 类。 要访问它,请稍后/下方根据 C# 代码示例使用 EnumValueOptionsExtensions.Value 或 EnumValueOptionsExtensions.AutoEnrol。

    然后在同一个或另一个 proto 文件中,使用以下内容创建一个枚举:

    enum SystemRoleType {
      ReservedRole = 0 [(Value) = "SystemRoleType.ReservedRole", (AutoEnrol) = false];
      Administrator = 1001 [(Value) = "SystemRoleType.Administrator", (AutoEnrol) = false];
      Editor = 1002 [(Value) = "SystemRoleType.Editor", (AutoEnrol) = false];
      ContentCreator = 1003 [(Value) = "SystemRoleType.ContentCreator", (AutoEnrol) = false];
      User = 1004 [(Value) = "SystemRoleType.ContentCreator", (AutoEnrol) = true];
    }
    

    同样,这将通过 protobuf 自动生成生成一个 SystemRoleTypeReflection 类。

    然后在 C# 中,相应地包含您的命名空间(还包括:Google.Protobuf.Reflection),然后您可以在 C# 中执行以下操作

    EnumDescriptor SystemRoleTypeTypeEnumDescriptor = SystemRoleTypeReflection.Descriptor.FindTypeByName<EnumDescriptor>(typeof(SystemRoleType).Name);
    foreach (SystemRoleType system_role_type in Enum.GetValues(typeof(SystemRoleType)))
    {
        EnumValueDescriptor enum_value_descriptor = SystemRoleTypeTypeEnumDescriptor.FindValueByNumber((int)system_role_type);
        var selector_value = enum_value_descriptor.GetOptions().GetExtension<string>(EnumValueOptionsExtensions.Value);
        var auto_enrolment = enum_value_descriptor.GetOptions().GetExtension<bool>(EnumValueOptionsExtensions.AutoEnrol);
    }
    

    更新(2021-12-01): 我今天将一个小型 VS 解决方案放在一起,您可以在 Visual Studio Community 2022 (.Net 6 / C# 10) 中打开它并在控制台应用程序中运行示例:github.com/kibblewhite/EnumValueOptions-gRPC-Example

    【讨论】:

    • 未创建/没有命名空间到 EnumValueOptionsExtensions 的后订单
    • 嘿,Gardes,根据您在此处提供的信息量,我可以假设两件事中的一件。 1. 您尚未在 proto 文件中定义选项“csharp_namespace” - stackoverflow.com/questions/58669740/… 2. 可能 Grpc.Tools 包未正确配置或 nuget 包中不存在/缺失?您可以尝试其他方法,访问“obj”文件夹(BaseIntermediateOutputPath),查看是否生成了 *.cs 存根文件。
    • 最后一件事可能是您的 *.csproj 中缺少 protobuf xml 节点条目。 *.csproj 文件中的 protobuf 节点应如下所示:``` ``` 抱歉,就提供的信息量而言,这是我能提供的最好的了。
    • 其实我可以更进一步,我今天放了一个小小的VS解决方案,你可以在Visual Studio Community 2022(.Net 6 / C# 10)中打开它并在控制台中运行示例应用:github.com/kibblewhite/EnumValueOptions-gRPC-Example
    猜你喜欢
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多