From 72ec33676fd40ccfe719ace162fcf859ae9251bc Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Thu, 19 Nov 2015 17:13:38 +0000 Subject: Tidy up reflection in advance of attempting to implement DynamicMessage. There are corner cases where MessageDescriptor.{ClrType,Parser} will return null, and these are now documented. However, normally they *should* be implemented, even for descriptors of for dynamic messages. Ditto FieldDescriptor.Accessor. We'll still need a fair amount of work to implement dynamic messages, but this change means that the public API will be remain intact. Additionally, this change starts making use of C# 6 features in the files that it touches. This is far from exhaustive, and later PRs will have more. Generated code changes coming in the next commit. --- .../Reflection/MessageDescriptor.cs | 162 +++++++++------------ 1 file changed, 71 insertions(+), 91 deletions(-) (limited to 'csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs') diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index 65040e42..769ac3d5 100644 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -56,153 +56,145 @@ namespace Google.Protobuf.Reflection "google/protobuf/type.proto", }; - private readonly DescriptorProto proto; - private readonly MessageDescriptor containingType; - private readonly IList nestedTypes; - private readonly IList enumTypes; private readonly IList fieldsInDeclarationOrder; private readonly IList fieldsInNumberOrder; private readonly IDictionary jsonFieldMap; - private readonly FieldCollection fields; - private readonly IList oneofs; - // CLR representation of the type described by this descriptor, if any. - private readonly Type generatedType; - private readonly MessageParser parser; internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, GeneratedCodeInfo generatedCodeInfo) : base(file, file.ComputeFullName(parent, proto.Name), typeIndex) { - this.proto = proto; - parser = generatedCodeInfo == null ? null : generatedCodeInfo.Parser; - generatedType = generatedCodeInfo == null ? null : generatedCodeInfo.ClrType; - - containingType = parent; - - oneofs = DescriptorUtil.ConvertAndMakeReadOnly( + Proto = proto; + Parser = generatedCodeInfo?.Parser; + ClrType = generatedCodeInfo?.ClrType; + ContainingType = parent; + + // Note use of generatedCodeInfo. rather than generatedCodeInfo?. here... we don't expect + // to see any nested oneofs, types or enums in "not actually generated" code... we do + // expect fields though (for map entry messages). + Oneofs = DescriptorUtil.ConvertAndMakeReadOnly( proto.OneofDecl, (oneof, index) => - new OneofDescriptor(oneof, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.OneofNames[index])); + new OneofDescriptor(oneof, file, this, index, generatedCodeInfo.OneofNames[index])); - nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly( + NestedTypes = DescriptorUtil.ConvertAndMakeReadOnly( proto.NestedType, (type, index) => - new MessageDescriptor(type, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedTypes[index])); + new MessageDescriptor(type, file, this, index, generatedCodeInfo.NestedTypes[index])); - enumTypes = DescriptorUtil.ConvertAndMakeReadOnly( + EnumTypes = DescriptorUtil.ConvertAndMakeReadOnly( proto.EnumType, (type, index) => - new EnumDescriptor(type, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.NestedEnums[index])); + new EnumDescriptor(type, file, this, index, generatedCodeInfo.NestedEnums[index])); fieldsInDeclarationOrder = DescriptorUtil.ConvertAndMakeReadOnly( proto.Field, (field, index) => - new FieldDescriptor(field, file, this, index, generatedCodeInfo == null ? null : generatedCodeInfo.PropertyNames[index])); + new FieldDescriptor(field, file, this, index, generatedCodeInfo?.PropertyNames[index])); fieldsInNumberOrder = new ReadOnlyCollection(fieldsInDeclarationOrder.OrderBy(field => field.FieldNumber).ToArray()); // TODO: Use field => field.Proto.JsonName when we're confident it's appropriate. (And then use it in the formatter, too.) jsonFieldMap = new ReadOnlyDictionary(fieldsInNumberOrder.ToDictionary(field => JsonFormatter.ToCamelCase(field.Name))); file.DescriptorPool.AddSymbol(this); - fields = new FieldCollection(this); - } - - /// - /// Returns the total number of nested types and enums, recursively. - /// - private int CountTotalGeneratedTypes() - { - return nestedTypes.Sum(nested => nested.CountTotalGeneratedTypes()) + enumTypes.Count; + Fields = new FieldCollection(this); } /// /// The brief name of the descriptor's target. /// - public override string Name { get { return proto.Name; } } + public override string Name => Proto.Name; - internal DescriptorProto Proto { get { return proto; } } + internal DescriptorProto Proto { get; } /// - /// The generated type for this message, or null if the descriptor does not represent a generated type. + /// The CLR type used to represent message instances from this descriptor. /// - public Type GeneratedType { get { return generatedType; } } + /// + /// + /// The value returned by this property will be non-null for all regular fields. However, + /// if a message containing a map field is introspected, the list of nested messages will include + /// an auto-generated nested key/value pair message for the field. This is not represented in any + /// generated type, so this property will return null in such cases. + /// + /// + /// For wrapper types ( and the like), the type returned here + /// will be the generated message type, not the native type used by reflection for fields of those types. Code + /// using reflection should call to determine whether a message descriptor represents + /// a wrapper type, and handle the result appropriately. + /// + /// + public Type ClrType { get; } /// /// A parser for this message type. /// /// + /// /// As is not generic, this cannot be statically - /// typed to the relevant type, but if returns a non-null value, the parser returned + /// typed to the relevant type, but it should produce objects of a type compatible with . + /// + /// + /// The value returned by this property will be non-null for all regular fields. However, + /// if a message containing a map field is introspected, the list of nested messages will include + /// an auto-generated nested key/value pair message for the field. No message parser object is created for + /// such messages, so this property will return null in such cases. + /// + /// + /// For wrapper types ( and the like), the parser returned here + /// will be the generated message type, not the native type used by reflection for fields of those types. Code + /// using reflection should call to determine whether a message descriptor represents + /// a wrapper type, and handle the result appropriately. + /// /// - public MessageParser Parser { get { return parser; } } + public MessageParser Parser { get; } /// /// Returns whether this message is one of the "well known types" which may have runtime/protoc support. /// - internal bool IsWellKnownType - { - get - { - return File.Package == "google.protobuf" && WellKnownTypeNames.Contains(File.Name); - } - } + internal bool IsWellKnownType => File.Package == "google.protobuf" && WellKnownTypeNames.Contains(File.Name); + + /// + /// Returns whether this message is one of the "wrapper types" used for fields which represent primitive values + /// with the addition of presence. + /// + internal bool IsWrapperType => File.Package == "google.protobuf" && File.Name == "google/protobuf/wrappers.proto"; /// /// If this is a nested type, get the outer descriptor, otherwise null. /// - public MessageDescriptor ContainingType - { - get { return containingType; } - } + public MessageDescriptor ContainingType { get; } /// /// A collection of fields, which can be retrieved by name or field number. /// - public FieldCollection Fields - { - get { return fields; } - } + public FieldCollection Fields { get; } /// /// An unmodifiable list of this message type's nested types. /// - public IList NestedTypes - { - get { return nestedTypes; } - } + public IList NestedTypes { get; } /// /// An unmodifiable list of this message type's enum types. /// - public IList EnumTypes - { - get { return enumTypes; } - } + public IList EnumTypes { get; } /// /// An unmodifiable list of the "oneof" field collections in this message type. /// - public IList Oneofs - { - get { return oneofs; } - } + public IList Oneofs { get; } /// /// Finds a field by field name. /// /// The unqualified name of the field (e.g. "foo"). /// The field's descriptor, or null if not found. - public FieldDescriptor FindFieldByName(String name) - { - return File.DescriptorPool.FindSymbol(FullName + "." + name); - } + public FieldDescriptor FindFieldByName(String name) =>File.DescriptorPool.FindSymbol(FullName + "." + name); /// /// Finds a field by field number. /// /// The field number within this message type. /// The field's descriptor, or null if not found. - public FieldDescriptor FindFieldByNumber(int number) - { - return File.DescriptorPool.FindFieldByNumber(this, number); - } + public FieldDescriptor FindFieldByNumber(int number) => File.DescriptorPool.FindFieldByNumber(this, number); /// /// Finds a nested descriptor by name. The is valid for fields, nested @@ -210,18 +202,15 @@ namespace Google.Protobuf.Reflection /// /// The unqualified name of the descriptor, e.g. "Foo" /// The descriptor, or null if not found. - public T FindDescriptor(string name) - where T : class, IDescriptor - { - return File.DescriptorPool.FindSymbol(FullName + "." + name); - } + public T FindDescriptor(string name) where T : class, IDescriptor => + File.DescriptorPool.FindSymbol(FullName + "." + name); /// /// Looks up and cross-links all fields and nested types. /// internal void CrossLink() { - foreach (MessageDescriptor message in nestedTypes) + foreach (MessageDescriptor message in NestedTypes) { message.CrossLink(); } @@ -231,7 +220,7 @@ namespace Google.Protobuf.Reflection field.CrossLink(); } - foreach (OneofDescriptor oneof in oneofs) + foreach (OneofDescriptor oneof in Oneofs) { oneof.CrossLink(); } @@ -253,10 +242,7 @@ namespace Google.Protobuf.Reflection /// Returns the fields in the message as an immutable list, in the order in which they /// are declared in the source .proto file. /// - public IList InDeclarationOrder() - { - return messageDescriptor.fieldsInDeclarationOrder; - } + public IList InDeclarationOrder() => messageDescriptor.fieldsInDeclarationOrder; /// /// Returns the fields in the message as an immutable list, in ascending field number @@ -264,10 +250,7 @@ namespace Google.Protobuf.Reflection /// index in the list to the field number; to retrieve a field by field number, it is better /// to use the indexer. /// - public IList InFieldNumberOrder() - { - return messageDescriptor.fieldsInNumberOrder; - } + public IList InFieldNumberOrder() => messageDescriptor.fieldsInNumberOrder; // TODO: consider making this public in the future. (Being conservative for now...) @@ -276,10 +259,7 @@ namespace Google.Protobuf.Reflection /// in the JSON representation to the field descriptors. For example, a field foo_bar /// in the message would result in an entry with a key fooBar. /// - internal IDictionary ByJsonName() - { - return messageDescriptor.jsonFieldMap; - } + internal IDictionary ByJsonName() => messageDescriptor.jsonFieldMap; /// /// Retrieves the descriptor for the field with the given number. -- cgit v1.2.3