File: System\Reflection\TypeLoading\RuntimeTypeInfo.GetMember.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.Diagnostics;
using System.Reflection.Runtime.BindingFlagSupport;
 
namespace System.Reflection.TypeLoading
{
    internal partial class RoType
    {
        public sealed override MemberInfo[] GetMembers(BindingFlags bindingAttr) => GetMemberImpl(null, MemberTypes.All, bindingAttr);
        public sealed override MemberInfo[] GetMember(string name, BindingFlags bindingAttr)
        {
            if (name is null)
            {
                throw new ArgumentNullException(nameof(name));
            }
 
            return GetMemberImpl(name, MemberTypes.All, bindingAttr);
        }
 
        public sealed override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr)
        {
            if (name is null)
            {
                throw new ArgumentNullException(nameof(name));
            }
 
            return GetMemberImpl(name, type, bindingAttr);
        }
 
        private MemberInfo[] GetMemberImpl(string? optionalNameOrPrefix, MemberTypes type, BindingFlags bindingAttr)
        {
            bool prefixSearch = optionalNameOrPrefix != null && optionalNameOrPrefix.EndsWith("*", StringComparison.Ordinal);
            string? optionalName = prefixSearch ? null : optionalNameOrPrefix;
 
            Func<MemberInfo, bool>? predicate = null;
            if (prefixSearch)
            {
                bool ignoreCase = (bindingAttr & BindingFlags.IgnoreCase) != 0;
                StringComparison comparisonType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
                string prefix = optionalNameOrPrefix!.Substring(0, optionalNameOrPrefix.Length - 1);
 
                predicate = (member => member.Name.StartsWith(prefix, comparisonType));
            }
 
            MemberInfo[]? results;
 
            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Method, out QueryResult<MethodInfo> methods)) != null)
                return results;
            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Constructor, out QueryResult<ConstructorInfo> constructors)) != null)
                return results;
            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Property, out QueryResult<PropertyInfo> properties)) != null)
                return results;
            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Event, out QueryResult<EventInfo> events)) != null)
                return results;
            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.Field, out QueryResult<FieldInfo> fields)) != null)
                return results;
            if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.NestedType, out QueryResult<Type> nestedTypes)) != null)
                return results;
            if ((type & (MemberTypes.NestedType | MemberTypes.TypeInfo)) == MemberTypes.TypeInfo)
            {
                if ((results = QuerySpecificMemberTypeIfRequested(type, optionalName, bindingAttr, predicate, MemberTypes.TypeInfo, out nestedTypes)) != null)
                    return results;
            }
 
            int numMatches = methods.Count + constructors.Count + properties.Count + events.Count + fields.Count + nestedTypes.Count;
            results = (type == (MemberTypes.Method | MemberTypes.Constructor)) ? new MethodBase[numMatches] : new MemberInfo[numMatches];
            int numCopied = 0;
 
            methods.CopyTo(results, numCopied);
            numCopied += methods.Count;
 
            constructors.CopyTo(results, numCopied);
            numCopied += constructors.Count;
 
            properties.CopyTo(results, numCopied);
            numCopied += properties.Count;
 
            events.CopyTo(results, numCopied);
            numCopied += events.Count;
 
            fields.CopyTo(results, numCopied);
            numCopied += fields.Count;
 
            nestedTypes.CopyTo(results, numCopied);
            numCopied += nestedTypes.Count;
 
            Debug.Assert(numCopied == numMatches);
 
            return results;
        }
 
        private M[]? QuerySpecificMemberTypeIfRequested<M>(MemberTypes memberType, string? optionalName, BindingFlags bindingAttr, Func<MemberInfo, bool>? optionalPredicate, MemberTypes targetMemberType, out QueryResult<M> queryResult) where M : MemberInfo
        {
            if ((memberType & targetMemberType) == 0)
            {
                // This type of member was not requested.
                queryResult = default;
                return null;
            }
 
            queryResult = Query<M>(optionalName, bindingAttr, optionalPredicate);
 
            // .NET Framework compat: If exactly one type of member was requested, the returned array has to be of that specific type (M[], not MemberInfo[]). Create it now and return it
            // to cause GetMember() to short-cut the search.
            if ((memberType & ~targetMemberType) == 0)
                return queryResult.ToArray();
 
            // .NET Framework compat: If we got here, than one MemberType was requested. Return null to signal GetMember() to keep querying the other member types and concatenate the results.
            return null;
        }
    }
}