File: System\Reflection\TypeLoading\Properties\RoProperty.cs
Web Access
Project: src\src\libraries\System.Reflection.MetadataLoadContext\src\System.Reflection.MetadataLoadContext.csproj (System.Reflection.MetadataLoadContext)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
 
namespace System.Reflection.TypeLoading
{
    /// <summary>
    /// Base class for all PropertyInfo objects created by a MetadataLoadContext.
    /// </summary>
    internal abstract partial class RoProperty : LeveledPropertyInfo
    {
        private readonly RoInstantiationProviderType _declaringType;
        private readonly Type _reflectedType;
 
        protected RoProperty(RoInstantiationProviderType declaringType, Type reflectedType)
        {
            Debug.Assert(declaringType != null);
            Debug.Assert(reflectedType != null);
 
            _declaringType = declaringType;
            _reflectedType = reflectedType;
        }
 
        public abstract override bool Equals(object? obj);
        public abstract override int GetHashCode();
        public abstract override string ToString();
 
        public sealed override Type DeclaringType => GetRoDeclaringType();
        internal RoInstantiationProviderType GetRoDeclaringType() => _declaringType;
 
        public sealed override Type ReflectedType => _reflectedType;
 
        public sealed override string Name => _lazyName ??= ComputeName();
        protected abstract string ComputeName();
        private volatile string? _lazyName;
 
        public sealed override Module Module => GetRoModule();
        internal abstract RoModule GetRoModule();
 
        public abstract override int MetadataToken { get; }
        public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => this.HasSameMetadataDefinitionAsCore(other);
 
        public sealed override IList<CustomAttributeData> GetCustomAttributesData() => CustomAttributes.ToReadOnlyCollection();
        public abstract override IEnumerable<CustomAttributeData> CustomAttributes { get; }
 
        public sealed override PropertyAttributes Attributes => (_lazyPropertyAttributes == PropertyAttributesSentinel) ? (_lazyPropertyAttributes = ComputeAttributes()) : _lazyPropertyAttributes;
        protected abstract PropertyAttributes ComputeAttributes();
        private const PropertyAttributes PropertyAttributesSentinel = (PropertyAttributes)(-1);
        private volatile PropertyAttributes _lazyPropertyAttributes = PropertyAttributesSentinel;
 
        public sealed override Type PropertyType
        {
            get
            {
                InitializeFieldType();
                return _lazyPropertyType!;
            }
        }
 
        protected RoModifiedType ModifiedType
        {
            get
            {
                InitializeFieldType();
                _modifiedType ??= RoModifiedType.Create((RoType)PropertyType);
                return _modifiedType;
            }
        }
 
        private void InitializeFieldType()
        {
            if (_lazyPropertyType is null)
            {
                Type type = ComputePropertyType();
                if (type is RoModifiedType modifiedType)
                {
                    _modifiedType = modifiedType;
                    _lazyPropertyType = modifiedType.UnderlyingSystemType;
                }
                else
                {
                    _lazyPropertyType = type;
                }
            }
        }
 
        protected abstract Type ComputePropertyType();
        private volatile Type? _lazyPropertyType;
        protected volatile RoModifiedType? _modifiedType;
 
        public sealed override MethodInfo? GetGetMethod(bool nonPublic) => GetRoGetMethod()?.FilterAccessor(nonPublic);
        public sealed override MethodInfo? GetSetMethod(bool nonPublic) => GetRoSetMethod()?.FilterAccessor(nonPublic);
 
        private RoMethod? GetRoGetMethod() => object.ReferenceEquals(_lazyGetter, Sentinels.RoMethod) ? (_lazyGetter = ComputeGetterMethod()?.FilterInheritedAccessor()) : _lazyGetter;
        private RoMethod? GetRoSetMethod() => object.ReferenceEquals(_lazySetter, Sentinels.RoMethod) ? (_lazySetter = ComputeSetterMethod()?.FilterInheritedAccessor()) : _lazySetter;
 
        protected abstract RoMethod? ComputeGetterMethod();
        protected abstract RoMethod? ComputeSetterMethod();
 
        private volatile RoMethod? _lazyGetter = Sentinels.RoMethod;
        private volatile RoMethod? _lazySetter = Sentinels.RoMethod;
 
        public sealed override bool CanRead => GetMethod != null;
        public sealed override bool CanWrite => SetMethod != null;
 
        public sealed override MethodInfo[] GetAccessors(bool nonPublic)
        {
            MethodInfo? getter = GetGetMethod(nonPublic);
            MethodInfo? setter = GetSetMethod(nonPublic);
 
            int count = 0;
            if (getter != null)
                count++;
            if (setter != null)
                count++;
 
            MethodInfo[] accessors = new MethodInfo[count];
            int index = 0;
            if (getter != null)
                accessors[index++] = getter;
            if (setter != null)
                accessors[index++] = setter;
 
            return accessors;
        }
 
        public sealed override ParameterInfo[] GetIndexParameters() => (_lazyIndexedParameters ??= ComputeIndexParameters()).CloneArray<ParameterInfo>();
        private RoPropertyIndexParameter[] ComputeIndexParameters()
        {
            bool useGetter = CanRead;
            RoMethod? accessor = (useGetter ? GetRoGetMethod() : GetRoSetMethod());
            if (accessor == null)
                throw new BadImageFormatException(); // Property has neither a getter or setter.
            RoParameter[] methodParameters = accessor.GetParametersNoCopy();
            int count = methodParameters.Length;
            if (!useGetter)
                count--;  // If we're taking the parameters off the setter, subtract one for the "value" parameter.
            if (count == 0)
                return Array.Empty<RoPropertyIndexParameter>();
 
            RoPropertyIndexParameter[] indexParameters = new RoPropertyIndexParameter[count];
            for (int i = 0; i < count; i++)
            {
                indexParameters[i] = new RoPropertyIndexParameter(this, methodParameters[i]);
            }
            return indexParameters;
        }
        private volatile RoPropertyIndexParameter[]? _lazyIndexedParameters;
 
        public sealed override object? GetRawConstantValue()
        {
            if ((Attributes & PropertyAttributes.HasDefault) == 0)
                throw new InvalidOperationException(SR.Arg_EnumLitValueNotFound);
 
            return ComputeRawConstantValue();
        }
 
        protected abstract object? ComputeRawConstantValue();
 
        public abstract override Type[] GetOptionalCustomModifiers();
        public abstract override Type[] GetRequiredCustomModifiers();
 
        // Operations that are not allowed for Reflection-only.
        public sealed override object[] GetCustomAttributes(bool inherit) => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
        public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
        public sealed override bool IsDefined(Type attributeType, bool inherit) => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
        public sealed override object GetConstantValue() => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
        public sealed override object GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
        public sealed override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
 
        internal TypeContext TypeContext => _declaringType.Instantiation.ToTypeContext();
    }
}