枚举类型
定义消息类型时,可能希望其中一个字段只包含预定义值列表中的一个。例如,假设您想为每个SearchRequest添加一个corpus
(语料库)字段,其中语料库的值可以是UNIVERSAL、WEB、IMAGES、LOCAL、NEWS、PRODUCTS或VIDEO。您只需在消息定义中添加一个枚举,每个可能的值都有一个常量,就可以做到这一点。
在下面的示例中,我们添加了一个名为Corpus的枚举,其中包含所有可能的值,以及一个类型为Corpus的字段:
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
如您所见,Corpus
枚举的第一个常量映射到零:每个枚举定义必须包含一个映射到零的常量作为其第一个元素。这是因为:
可以通过将相同的值赋给不同的枚举常量来定义别名。为此,您需要将allow_alias
选项设置为true
,否则协议编译器将在找到别名时将生成错误消息。
message MyMessage1 {
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
message MyMessage2 {
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}
}
枚举器常量必须在32位整数的范围内。由于enum
使用可变编码,因此负值效率很低,所以不建议使用。您可以在定义的消息内部定义枚举,如上面的示例所示,也可以在外部定义枚举——这些枚举可以在.proto
文件中的任何消息定义中重用。您还可以使用_MessageType_._EnumType_
语法将一条消息中声明的枚举类型用作另一条消息中的字段类型。
当使用协议缓冲区编译器编译一个使用了枚举
的.proto
文件时,对于Java
和C++
来说,会生成一个对应的枚举类型;对于Python
,会生成一个特殊EnumDescriptor
类,用于在运行时生成的类中创建一组具有整数值的符号常量。
警告:生成的代码可能会受到特定语言的枚举数限制(一种语言的枚举数低千)。请检查您计划使用的语言的限制。
在反序列化过程中,无法识别的枚举值将保留在消息中,尽管反序列化消息时如何表示这些值取决于语言。在支持具有指定枚举范围以外值的开枚举类型的语言中,例如C++
和GO
,未知的枚举值被简单地存储为其基础整数表示形式。在具有封闭枚举类型的语言(如Java)中,枚举中的大小写用于表示无法识别的值,并且可以使用特殊的访问器访问基础整数。在这两种情况下,如果消息被序列化,则无法识别的值仍将与消息一起序列化。
有关如何在应用程序中使用消息枚举的详细信息,请参阅所选语言的生成代码指南。
保留值
如果通过完全删除枚举条目或把它注释掉来更新枚举类型,则将来的用户可以在自己更新该类型时重用该数值。如果以后加载相同.proto
的旧版本,这可能会导致严重的数据损坏、隐私漏洞等问题。确保不会发生这种情况的一种方法是指定保留已删除条目的数值(和/或名称,这也可能导致JSON序列化问题)。如果将来有任何用户试图使用这些标识符,协议缓冲区编译器就会报错。您可以使用max
关键字指定您的保留数值范围提高到可能的最大值。
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
请注意,不能在同一个
reserved
语句中混合使用字段名和数值。