File: System\Reflection\TypeLoading\RuntimeTypeInfo.BindingFlags.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;
using System.Reflection.Runtime.General;
 
namespace System.Reflection.TypeLoading
{
    internal partial class RoType
    {
        public sealed override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => Query<ConstructorInfo>(bindingAttr).ToArray();
 
        protected sealed override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers)
        {
            Debug.Assert(types != null);
 
            QueryResult<ConstructorInfo> queryResult = Query<ConstructorInfo>(bindingAttr);
            ListBuilder<ConstructorInfo> candidates = default;
            foreach (ConstructorInfo candidate in queryResult)
            {
                if (candidate.QualifiesBasedOnParameterCount(bindingAttr, callConvention, types))
                    candidates.Add(candidate);
            }
 
            // For perf and .NET Framework compat, fast-path these specific checks before calling on the binder to break ties.
            if (candidates.Count == 0)
                return null;
 
            if (types.Length == 0 && candidates.Count == 1)
            {
                ConstructorInfo firstCandidate = candidates[0];
                ParameterInfo[] parameters = firstCandidate.GetParametersNoCopy();
                if (parameters.Length == 0)
                    return firstCandidate;
            }
 
            if ((bindingAttr & BindingFlags.ExactBinding) != 0)
                return System.DefaultBinder.ExactBinding(candidates.ToArray(), types) as ConstructorInfo;
 
            binder ??= Loader.GetDefaultBinder();
 
            return binder.SelectMethod(bindingAttr, candidates.ToArray(), types, modifiers) as ConstructorInfo;
        }
 
        public sealed override EventInfo[] GetEvents(BindingFlags bindingAttr) => Query<EventInfo>(bindingAttr).ToArray();
        public sealed override EventInfo? GetEvent(string name, BindingFlags bindingAttr) => Query<EventInfo>(name, bindingAttr).Disambiguate();
 
        public sealed override FieldInfo[] GetFields(BindingFlags bindingAttr) => Query<FieldInfo>(bindingAttr).ToArray();
        public sealed override FieldInfo? GetField(string name, BindingFlags bindingAttr) => Query<FieldInfo>(name, bindingAttr).Disambiguate();
 
        public sealed override MethodInfo[] GetMethods(BindingFlags bindingAttr) => Query<MethodInfo>(bindingAttr).ToArray();
 
        protected sealed override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers)
        {
            return GetMethodImplCommon(name, GenericParameterCountAny, bindingAttr, binder, callConvention, types, modifiers);
        }
 
        protected sealed override MethodInfo? GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers)
        {
            return GetMethodImplCommon(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers);
        }
 
        private MethodInfo? GetMethodImplCommon(string name, int genericParameterCount, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers)
        {
            Debug.Assert(name != null);
 
            // GetMethodImpl() is a funnel for two groups of api. We can distinguish by comparing "types" to null.
            if (types == null)
            {
                // Group #1: This group of api accept only a name and BindingFlags. The other parameters are hard-wired by the non-virtual api entrypoints.
                Debug.Assert(genericParameterCount == GenericParameterCountAny);
                Debug.Assert(binder == null);
                Debug.Assert(callConvention == CallingConventions.Any);
                Debug.Assert(modifiers == null);
                return Query<MethodInfo>(name, bindingAttr).Disambiguate();
            }
            else
            {
                // Group #2: This group of api takes a set of parameter types and an optional binder.
                QueryResult<MethodInfo> queryResult = Query<MethodInfo>(name, bindingAttr);
                ListBuilder<MethodInfo> candidates = default;
                foreach (MethodInfo candidate in queryResult)
                {
                    if (genericParameterCount != GenericParameterCountAny && genericParameterCount != candidate.GetGenericParameterCount())
                        continue;
                    if (candidate.QualifiesBasedOnParameterCount(bindingAttr, callConvention, types))
                        candidates.Add(candidate);
                }
 
                if (candidates.Count == 0)
                    return null;
 
                // For perf and .NET Framework compat, fast-path these specific checks before calling on the binder to break ties.
                if (types.Length == 0 && candidates.Count == 1)
                    return candidates[0];
 
                binder ??= Loader.GetDefaultBinder();
                return binder.SelectMethod(bindingAttr, candidates.ToArray(), types, modifiers) as MethodInfo;
            }
        }
 
        public sealed override Type[] GetNestedTypes(BindingFlags bindingAttr) => Query<Type>(bindingAttr).ToArray();
        public sealed override Type? GetNestedType(string name, BindingFlags bindingAttr) => Query<Type>(name, bindingAttr).Disambiguate();
 
        public sealed override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => Query<PropertyInfo>(bindingAttr).ToArray();
 
        protected sealed override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers)
        {
            Debug.Assert(name != null);
 
            // GetPropertyImpl() is a funnel for two groups of api. We can distinguish by comparing "types" to null.
            if (types == null && returnType == null)
            {
                // Group #1: This group of api accept only a name and BindingFlags. The other parameters are hard-wired by the non-virtual api entrypoints.
                Debug.Assert(binder == null);
                Debug.Assert(modifiers == null);
                return Query<PropertyInfo>(name, bindingAttr).Disambiguate();
            }
            else
            {
                // Group #2: This group of api takes a set of parameter types, a return type (both cannot be null) and an optional binder.
                QueryResult<PropertyInfo> queryResult = Query<PropertyInfo>(name, bindingAttr);
                ListBuilder<PropertyInfo> candidates = default;
                foreach (PropertyInfo candidate in queryResult)
                {
                    if (types == null || (candidate.GetIndexParameters().Length == types.Length))
                    {
                        candidates.Add(candidate);
                    }
                }
 
                if (candidates.Count == 0)
                    return null;
 
                // For perf and .NET Framework compat, fast-path these specific checks before calling on the binder to break ties.
                if (types == null || types.Length == 0)
                {
                    PropertyInfo firstCandidate = candidates[0];
 
                    // no arguments
                    if (candidates.Count == 1)
                    {
                        if (!(returnType is null) && !returnType.IsEquivalentTo(firstCandidate.PropertyType))
                            return null;
                        return firstCandidate;
                    }
                    else
                    {
                        if (returnType is null)
                            // if we are here we have no args or property type to select over and we have more than one property with that name
                            throw ThrowHelper.GetAmbiguousMatchException(firstCandidate);
                    }
                }
 
                if ((bindingAttr & BindingFlags.ExactBinding) != 0)
                    return System.DefaultBinder.ExactPropertyBinding(candidates.ToArray(), returnType, types);
 
                binder ??= Loader.GetDefaultBinder();
 
                return binder.SelectProperty(bindingAttr, candidates.ToArray(), returnType, types, modifiers);
            }
        }
 
        private QueryResult<M> Query<M>(BindingFlags bindingAttr) where M : MemberInfo
        {
            return Query<M>(null, bindingAttr, null);
        }
 
        private QueryResult<M> Query<M>(string name, BindingFlags bindingAttr) where M : MemberInfo
        {
            if (name is null)
            {
                throw new ArgumentNullException(nameof(name));
            }
 
            return Query<M>(name, bindingAttr, null);
        }
 
        private QueryResult<M> Query<M>(string? optionalName, BindingFlags bindingAttr, Func<M, bool>? optionalPredicate) where M : MemberInfo
        {
            MemberPolicies<M> policies = MemberPolicies<M>.Default;
            bindingAttr = policies.ModifyBindingFlags(bindingAttr);
            bool immediateTypeOnly = NeedToSearchImmediateTypeOnly(bindingAttr);
            bool ignoreCase = (bindingAttr & BindingFlags.IgnoreCase) != 0;
 
            TypeComponentsCache cache = Cache;
            QueriedMemberList<M> queriedMembers;
            if (optionalName == null)
                queriedMembers = cache.GetQueriedMembers<M>(immediateTypeOnly);
            else
                queriedMembers = cache.GetQueriedMembers<M>(optionalName, ignoreCase: ignoreCase, immediateTypeOnly: immediateTypeOnly);
 
            if (optionalPredicate != null)
                queriedMembers = queriedMembers.Filter(optionalPredicate);
            return new QueryResult<M>(bindingAttr, queriedMembers);
        }
 
        private static bool NeedToSearchImmediateTypeOnly(BindingFlags bf)
        {
            if ((bf & (BindingFlags.Static | BindingFlags.FlattenHierarchy)) == (BindingFlags.Static | BindingFlags.FlattenHierarchy))
                return false;
 
            if ((bf & (BindingFlags.Instance | BindingFlags.DeclaredOnly)) == BindingFlags.Instance)
                return false;
 
            return true;
        }
 
        private TypeComponentsCache Cache => _lazyCache ??= new TypeComponentsCache(this);
 
        private volatile TypeComponentsCache? _lazyCache;
 
        private const int GenericParameterCountAny = -1;
    }
}