diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h index d295bbe803..90ead89c11 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.h +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h @@ -159,24 +159,23 @@ inline bool IsProto2(const FileDescriptor* descriptor) { } inline bool SupportsPresenceApi(const FieldDescriptor* descriptor) { - // We don't use descriptor->is_singular_with_presence() as C# has slightly - // different behavior to other languages. - - if (IsProto2(descriptor->file())) { - // We generate Has/Clear for oneof fields in C#, as well as for messages. - // It's possible that we shouldn't, but stopping doing so would be a - // breaking change for proto2. Fortunately the decision is moot for - // onoeof in proto3: you can't use "optional" inside a oneof. - // Proto2: every singular field has presence. (Even fields in oneofs.) - return !descriptor->is_repeated(); - } else { - // Proto3: only for explictly-optional fields that aren't messages. - // (Repeated fields can never be explicitly optional, so we don't need - // to check for that.) Currently we can't get at proto3_optional directly, - // but we can use has_optional_keyword() as a surrogate check. - return descriptor->has_optional_keyword() && - descriptor->type() != FieldDescriptor::TYPE_MESSAGE; + // Unlike most languages, we don't generate Has/Clear members for message + // types, because they can always be set to null in C#. They're not really + // needed for oneof fields in proto2 either, as everything can be done via + // oneof case, but we follow the convention from other languages. Proto3 + // oneof fields never have Has/Clear members - but will also never have + // the explicit optional keyword either. + // + // None of the built-in helpers (descriptor->has_presence() etc) describe + // quite the behavior we want, so the rules are explicit below. + + if (descriptor->is_repeated() || + descriptor->type() == FieldDescriptor::TYPE_MESSAGE) { + return false; } + // has_optional_keyword() has more complex rules for proto2, but that + // doesn't matter given the first part of this condition. + return IsProto2(descriptor->file()) || descriptor->has_optional_keyword(); } inline bool RequiresPresenceBit(const FieldDescriptor* descriptor) {