internal AttributedProperty(PropertyInfo property, FirestorePropertyAttribute attribute)
            {
                _propertyInfo  = property;
                FirestoreName  = attribute.Name ?? property.Name;
                _sentinelValue = SentinelValue.FromPropertyAttributes(property);
                if (attribute.ConverterType != null)
                {
                    _converter = CustomConverter.ForConverterType(attribute.ConverterType, property.PropertyType);
                }

                // Note that the error messages in here don't use nameof, as we don't have an overload of CheckState accepting three
                // format arguments.
                // TODO: Put that in GAX and use nameof.
                string typeName = property.DeclaringType.FullName;

                GaxPreconditions.CheckState(property.GetIndexParameters().Length == 0,
                                            "{0}.{1} is an indexer, and should not be decorated with FirestorePropertyAttribute.",
                                            typeName, property.Name);

                // Annoyingly, we can't easily check whether the property is static - we have to check the individual methods.
                var getMethod = property.GetGetMethod(nonPublic: true);
                var setMethod = property.GetSetMethod(nonPublic: true);

                GaxPreconditions.CheckState(getMethod == null || !getMethod.IsStatic,
                                            "{0}.{1} is static, and should not be decorated with FirestorePropertyAttribute.",
                                            typeName, property.Name);
                GaxPreconditions.CheckState(setMethod == null || !setMethod.IsStatic,
                                            "{0}.{1} is static, and should not be decorated with FirestorePropertyAttribute.",
                                            typeName, property.Name);
            }
            internal AttributedProperty(PropertyInfo property, FirestorePropertyAttribute attribute)
            {
                _propertyInfo  = property;
                FirestoreName  = attribute.Name ?? property.Name;
                _sentinelValue = SentinelValue.FromPropertyAttributes(property);
                if (attribute.ConverterType != null)
                {
                    _converter = CustomConverter.ForConverterType(attribute.ConverterType, property.PropertyType);
                }

                string typeName = property.DeclaringType.FullName;

                GaxPreconditions.CheckState(property.GetIndexParameters().Length == 0,
                                            "{0}.{1} is an indexer, and should not be decorated with {2}.",
                                            typeName, property.Name, nameof(FirestorePropertyAttribute));

                // Annoyingly, we can't easily check whether the property is static - we have to check the individual methods.
                var getMethod = property.GetGetMethod(nonPublic: true);
                var setMethod = property.GetSetMethod(nonPublic: true);

                GaxPreconditions.CheckState(getMethod == null || !getMethod.IsStatic,
                                            "{0}.{1} is static, and should not be decorated with {2}.",
                                            typeName, property.Name, nameof(FirestorePropertyAttribute));
                GaxPreconditions.CheckState(setMethod == null || !setMethod.IsStatic,
                                            "{0}.{1} is static, and should not be decorated with {2}.",
                                            typeName, property.Name, nameof(FirestorePropertyAttribute));
            }
        private AttributedTypeConverter(BclType targetType, FirestoreDataAttribute attribute) : base(targetType)
        {
            var typeInfo = targetType.GetTypeInfo();

            _attribute = attribute;

            // Check for user bugs in terms of attribute specifications.
            GaxPreconditions.CheckState(Enum.IsDefined(typeof(UnknownPropertyHandling), _attribute.UnknownPropertyHandling),
                                        "Type {0} has invalid {1} value", targetType.FullName, nameof(FirestoreDataAttribute.UnknownPropertyHandling));

            _ctor = typeInfo
                    .GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .SingleOrDefault(ctor => ctor.GetParameters().Length == 0);
            GaxPreconditions.CheckState(_ctor != null, "Type {0} has no parameterless constructor", targetType.FullName);

            List <AttributedProperty> readableProperties = new List <AttributedProperty>();
            Dictionary <string, AttributedProperty> writableProperties = new Dictionary <string, AttributedProperty>();

            // We look for static properties specifically to find problems. We'll never use static properties.
            foreach (var property in typeInfo.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
            {
                FirestorePropertyAttribute propertyAttribute = property.GetCustomAttribute <FirestorePropertyAttribute>(inherit: true);
                if (propertyAttribute == null)
                {
                    continue;
                }
                var    attributedProperty = new AttributedProperty(property, propertyAttribute);
                string firestoreName      = attributedProperty.FirestoreName;

                // Note that we check readable and writable properties separately. We could theoretically have
                // two separate properties, one read-only and one write-only, with the same name in Firestore.
                if (attributedProperty.CanRead)
                {
                    // This is O(N), but done once per type so should be okay.
                    GaxPreconditions.CheckState(!readableProperties.Any(p => p.FirestoreName == firestoreName),
                                                "Type {0} contains multiple readable properties with name {1}", targetType.FullName, firestoreName);
                    readableProperties.Add(attributedProperty);
                }
                if (attributedProperty.CanWrite)
                {
                    GaxPreconditions.CheckState(!writableProperties.ContainsKey(firestoreName),
                                                "Type {0} contains multiple writable properties with name {1}", targetType.FullName, firestoreName);
                    writableProperties[firestoreName] = attributedProperty;
                }
            }
            _readableProperties = readableProperties.AsReadOnly();
            _writableProperties = new ReadOnlyDictionary <string, AttributedProperty>(writableProperties);
        }