File: System\ComponentModel\Composition\ReflectionModel\LazyMemberInfo.cs
Web Access
Project: src\src\libraries\System.ComponentModel.Composition\src\System.ComponentModel.Composition.csproj (System.ComponentModel.Composition)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.Internal;
 
namespace System.ComponentModel.Composition.ReflectionModel
{
    public struct LazyMemberInfo : IEquatable<LazyMemberInfo>
    {
        private readonly MemberTypes _memberType;
        private MemberInfo?[]? _accessors;
        private readonly Func<MemberInfo[]>? _accessorsCreator;
 
        public LazyMemberInfo(MemberInfo member)
        {
            Requires.NotNull(member, nameof(member));
            EnsureSupportedMemberType(member.MemberType, nameof(member));
 
            _accessorsCreator = null;
            _memberType = member.MemberType;
 
            switch (_memberType)
            {
                case MemberTypes.Property:
                    PropertyInfo property = (PropertyInfo)member;
                    if (property == null)
                    {
                        throw new Exception(SR.Diagnostic_InternalExceptionMessage);
                    }
                    _accessors = new MemberInfo?[] { property.GetGetMethod(true), property.GetSetMethod(true) };
                    break;
                case MemberTypes.Event:
                    EventInfo event_ = (EventInfo)member;
                    _accessors = new MemberInfo?[] { event_.GetRaiseMethod(true), event_.GetAddMethod(true), event_.GetRemoveMethod(true) };
                    break;
                default:
                    _accessors = new MemberInfo[] { member };
                    break;
            }
        }
 
        public LazyMemberInfo(MemberTypes memberType, params MemberInfo[] accessors)
        {
            EnsureSupportedMemberType(memberType, nameof(memberType));
            Requires.NotNull(accessors, nameof(accessors));
 
            string errorMessage;
            if (!LazyMemberInfo.AreAccessorsValid(memberType, accessors, out errorMessage))
            {
                throw new ArgumentException(errorMessage, nameof(accessors));
            }
 
            _memberType = memberType;
            _accessors = accessors;
            _accessorsCreator = null;
        }
 
        public LazyMemberInfo(MemberTypes memberType, Func<MemberInfo[]> accessorsCreator)
        {
            EnsureSupportedMemberType(memberType, nameof(memberType));
            Requires.NotNull(accessorsCreator, nameof(accessorsCreator));
 
            _memberType = memberType;
            _accessors = null;
            _accessorsCreator = accessorsCreator;
        }
 
        public MemberTypes MemberType
        {
            get { return _memberType; }
        }
 
        public MemberInfo[] GetAccessors()
        {
            if ((_accessors == null) && (_accessorsCreator != null))
            {
                MemberInfo[] accessors = _accessorsCreator.Invoke();
 
                string errorMessage;
                if (!LazyMemberInfo.AreAccessorsValid(MemberType, accessors, out errorMessage))
                {
                    throw new InvalidOperationException(errorMessage);
                }
 
                _accessors = accessors;
            }
 
            return _accessors!;
        }
 
        public override int GetHashCode()
        {
            if (_accessorsCreator != null)
            {
                return MemberType.GetHashCode() ^ _accessorsCreator.GetHashCode();
            }
 
            if (_accessors != null && _accessors[0] is MemberInfo accessor)
            {
                return MemberType.GetHashCode() ^ accessor.GetHashCode();
            }
 
            throw new Exception(SR.Diagnostic_InternalExceptionMessage);
        }
 
        public override bool Equals(object? obj) =>
            obj is LazyMemberInfo other && Equals(other);
 
        /// <summary>Indicates whether the current instance is equal to another instance of the same type.</summary>
        /// <param name="other">An instance to compare with this instance.</param>
        /// <returns>true if the current instance is equal to the other instance; otherwise, false.</returns>
        public bool Equals(LazyMemberInfo other)
        {
            // Different member types mean different members
            if (_memberType != other._memberType)
            {
                return false;
            }
 
            // if any of the lazy members create accessors in a delay-loaded fashion, we simply compare the creators
            if ((_accessorsCreator != null) || (other._accessorsCreator != null))
            {
                return object.Equals(_accessorsCreator, other._accessorsCreator);
            }
 
            // we are dealing with explicitly passed accessors in both cases
            if (_accessors == null || other._accessors == null)
            {
                throw new Exception(SR.Diagnostic_InternalExceptionMessage);
            }
            return _accessors.SequenceEqual(other._accessors);
        }
 
        public static bool operator ==(LazyMemberInfo left, LazyMemberInfo right)
        {
            return left.Equals(right);
        }
 
        public static bool operator !=(LazyMemberInfo left, LazyMemberInfo right)
        {
            return !left.Equals(right);
        }
        private static void EnsureSupportedMemberType(MemberTypes memberType, string argument)
        {
            MemberTypes supportedTypes = MemberTypes.TypeInfo | MemberTypes.NestedType | MemberTypes.Constructor | MemberTypes.Field | MemberTypes.Method | MemberTypes.Property | MemberTypes.Event;
            Requires.IsInMembertypeSet(memberType, argument, supportedTypes);
        }
 
        private static bool AreAccessorsValid(MemberTypes memberType, MemberInfo[] accessors, out string errorMessage)
        {
            errorMessage = string.Empty;
            if (accessors == null)
            {
                errorMessage = SR.LazyMemberInfo_AccessorsNull;
                return false;
            }
 
            if (accessors.All(accessor => accessor == null))
            {
                errorMessage = SR.LazyMemberInfo_NoAccessors;
                return false;
            }
 
            switch (memberType)
            {
                case MemberTypes.Property:
                    if (accessors.Length != 2)
                    {
                        errorMessage = SR.LazyMemberInfo_InvalidPropertyAccessors_Cardinality;
                        return false;
                    }
 
                    if (accessors.Where(accessor => (accessor != null) && (accessor.MemberType != MemberTypes.Method)).Any())
                    {
                        errorMessage = SR.LazyMemberinfo_InvalidPropertyAccessors_AccessorType;
                        return false;
                    }
 
                    break;
 
                case MemberTypes.Event:
                    if (accessors.Length != 3)
                    {
                        errorMessage = SR.LazyMemberInfo_InvalidEventAccessors_Cardinality;
                        return false;
                    }
 
                    if (accessors.Where(accessor => (accessor != null) && (accessor.MemberType != MemberTypes.Method)).Any())
                    {
                        errorMessage = SR.LazyMemberinfo_InvalidEventAccessors_AccessorType;
                        return false;
                    }
 
                    break;
 
                default:
                    if (
                        (accessors.Length != 1) ||
                        ((accessors.Length == 1) && (accessors[0].MemberType != memberType)))
                    {
                        errorMessage = SR.Format(SR.LazyMemberInfo_InvalidAccessorOnSimpleMember, memberType);
                        return false;
                    }
 
                    break;
            }
            return true;
        }
    }
}