Proto3:Oneof

If you have a message with many fields and where at most one field will be set at the same time, you can enforce this behavior and save memory by using the oneof feature.
如果你有一個有很多字段的message,而這些字段至少有一個字段需要同時被設(shè)置,你可以使用oneof特性來強(qiáng)制執(zhí)行這種行為,并且可以節(jié)省內(nèi)存。

Oneof fields are like regular fields except all the fields in a oneof share memory, and at most one field can be set at the same time. Setting any member of the oneof automatically clears all the other members. You can check which value in a oneof is set (if any) using a special case() or WhichOneof() method, depending on your chosen language.
除了在oneof中的所有字段共享內(nèi)存并且至多一個字段可以被同時設(shè)置值之外,Oneof 字段和常規(guī)字段一樣。設(shè)置任何oneof的成員后都會自動清理其他所有成員。你可以通過特殊case()或WhichOneof()方法檢測oneof中哪個值被設(shè)置了(如果有的話),取決于你所選擇的語言。

Note that if multiple values are set, the last set value as determined by the order in the proto will overwrite all previous ones.
注意如果多個值被設(shè)置,最終哪個值被設(shè)置是由proto中的順序決定,并且會覆蓋之前的設(shè)置。

Using Oneof - 使用Oneof

To define a oneof in your .proto you use the oneof keyword followed by your oneof name, in this case test_oneof:
為了在你的.proto中定義一個oneof,你可以使用oneof關(guān)鍵字再加上你的oneof名稱,在test_oneof案例中:

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

You then add your oneof fields to the oneof definition. You can add fields of any type, except map fields and repeatedfields.
你在oneof定義中添加了你的oneof字段,除了map字段和repeated字段,你可以添加任意類型的字段。

In your generated code, oneof fields have the same getters and setters as regular fields. You also get a special method for checking which value (if any) in the oneof is set. You can find out more about the oneof API for your chosen language in the relevant API reference.
在你生成的代碼中,和常規(guī)字段一樣oneof字段也有g(shù)etter和setter方法。你也會活得一個特殊方法用于檢測oneof中哪個字段被設(shè)置了值(如果有的話)。在相關(guān)API 參考中你可以查看更多你所選語言的oneof API的信息

Oneof Features - Oneof 特性

  • Setting a oneof field will automatically clear all other members of the oneof. So if you set several oneof fields, only the last field you set will still have a value.
    設(shè)置一個oneof字段后會自動清除oneof所有其他成員的值。所以如果你設(shè)置了多個oneof字段,僅最后一個字段會有值。
SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
// Calling mutable_sub_message() will clear the name field and will set
// sub_message to a new instance of SubMessage with none of its fields set
message.mutable_sub_message();
CHECK(!message.has_name());
  • If the parser encounters multiple members of the same oneof on the wire, only the last member seen is used in the parsed message.
    如果解析器在線路中遇到同一oneof的多個成員,則在message解析中僅使用最后一個看到的成員。

  • A oneof cannot be repeated.
    oneof不可重復(fù)。

  • Reflection APIs work for oneof fields.
    Reflection APIs對oneof 字段同樣生效。

  • If you set a oneof field to the default value (such as setting an int32 oneof field to 0), the "case" of that oneof field will be set, and the value will be serialized on the wire.
    如果你講oneof 字段設(shè)置為默認(rèn)值(比如將一個int32類型的oneof字段設(shè)置為0),則oneof字段的“case”將會被設(shè)置,其值會在線路中被序列化。

  • If you're using C++, make sure your code doesn't cause memory crashes. The following sample code will crash because sub_message was already deleted by calling the set_name() method.
    如果你正在使用C++,保證你的代碼不會導(dǎo)致內(nèi)存崩潰。下面例子中簡單代碼就會崩潰,因?yàn)樵趕et_name()方法調(diào)用后sub_message已經(jīng)被刪除。

SampleMessage message;
SubMessage* sub_message = message.mutable_sub_message();
message.set_name("name");      // Will delete sub_message
sub_message->set_...            // Crashes here
  • Again in C++, if you Swap() two messages with oneofs, each message will end up with the other’s oneof case: in the example below, msg1 will have a sub_message and msg2 will have a name.
    同樣在C++中,如果你給兩個oneof message調(diào)用Swap(),每個message會以另一個message的oneof情況結(jié)束:在下面的例子中,msg1會擁有一個sub_message而msg2會有一個name。
SampleMessage msg1;
msg1.set_name("name");
SampleMessage msg2;
msg2.mutable_sub_message();
msg1.swap(&msg2);
CHECK(msg1.has_sub_message());
CHECK(msg2.has_name());

Backwards-compatibility issues - 向下兼容問題

Be careful when adding or removing oneof fields. If checking the value of a oneof returns None/NOT_SET, it could mean that the oneof has not been set or it has been set to a field in a different version of the oneof. There is no way to tell the difference, since there's no way to know if an unknown field on the wire is a member of the oneof.
添加或移除oneof 字段時需要當(dāng)心。如果檢測oneof字段值返回None/NOT_SET,這可能意味著oneof字段未被設(shè)置或者在一個不同版本中其中有一個字段已經(jīng)被設(shè)置。無法確定不同,因?yàn)闊o法知道線路中的一個未知字段是否是oneof的一個成員。

Tag Reuse Issues - 標(biāo)簽重用問題

Move fields into or out of a oneof: You may lose some of your information (some fields will be cleared) after the message is serialized and parsed. However, you can safely move a single field into a new oneof and may be able to move multiple fields if it is known that only one is ever set.
移入或移出字段到oneof:在messag被序列化或解析后,你可能會丟失你的某些信息(某些字段被清除)。然而,你可以安全地將一個單一字段移動到一個新的oneof中,也許能夠移動多個字段如果已知只有一個曾被設(shè)置過。

Delete a oneof field and add it back: This may clear your currently set oneof field after the message is serialized and parsed.
Split or merge oneof: This has similar issues to moving regular fields.
刪除一個oneof字段然后添加回去:在message被序列化或解析后,這樣做可能會清楚掉你當(dāng)前設(shè)置了的oneof 字段。
分開或合并oneof:這和移動常規(guī)字段面臨同樣的問題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容