File: System\Reflection\Runtime\BindingFlagSupport\Shared.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.TypeLoading;
 
namespace System.Reflection.Runtime.BindingFlagSupport
{
    internal static class Shared
    {
        //
        // This is similar to FilterApplyMethodBase from CoreClr with some important differences:
        //
        //   - Does *not* filter on Public|NonPublic|Instance|Static|FlatternHierarchy. Caller is expected to have done that.
        //
        //   - ArgumentTypes cannot be null.
        //
        // Used by Type.GetMethodImpl(), Type.GetConstructorImpl(), Type.InvokeMember() and Activator.CreateInstance(). Does some
        // preliminary weeding out of candidate methods based on the supplied calling convention and parameter list lengths.
        //
        // Candidates must pass this screen before we involve the binder.
        //
        public static bool QualifiesBasedOnParameterCount(this MethodBase methodBase, BindingFlags bindingFlags, CallingConventions callConv, Type[] argumentTypes)
        {
            Debug.Assert(methodBase != null);
            Debug.Assert(argumentTypes != null);
 
            #region Check CallingConvention
            if ((callConv & CallingConventions.Any) == 0)
            {
                if ((callConv & CallingConventions.VarArgs) != 0 && (methodBase.CallingConvention & CallingConventions.VarArgs) == 0)
                    return false;
 
                if ((callConv & CallingConventions.Standard) != 0 && (methodBase.CallingConvention & CallingConventions.Standard) == 0)
                    return false;
            }
            #endregion
 
            #region ArgumentTypes
            ParameterInfo[] parameterInfos = methodBase.GetParametersNoCopy();
 
            if (argumentTypes.Length != parameterInfos.Length)
            {
                #region Invoke Member, Get\Set & Create Instance specific case
                // If the number of supplied arguments differs than the number in the signature AND
                // we are not filtering for a dynamic call -- InvokeMethod or CreateInstance -- filter out the method.
                if ((bindingFlags & (BindingFlags.InvokeMethod | BindingFlags.CreateInstance | BindingFlags.GetProperty | BindingFlags.SetProperty)) == 0)
                    return false;
 
                throw new InvalidOperationException(SR.NoInvokeMember);
#endregion
            }
            else
            {
#region Exact Binding
                if ((bindingFlags & BindingFlags.ExactBinding) != 0)
                {
                    // Legacy behavior is to ignore ExactBinding when InvokeMember is specified.
                    // Why filter by InvokeMember? If the answer is we leave this to the binder then why not leave
                    // all the rest of this  to the binder too? Further, what other semanitc would the binder
                    // use for BindingFlags.ExactBinding besides this one? Further, why not include CreateInstance
                    // in this if statement? That's just InvokeMethod with a constructor, right?
                    if ((bindingFlags & (BindingFlags.InvokeMethod)) == 0)
                    {
                        for (int i = 0; i < parameterInfos.Length; i++)
                        {
                            // a null argument type implies a null arg which is always a perfect match
                            if (!(argumentTypes[i] is null) && !argumentTypes[i].MatchesParameterTypeExactly(parameterInfos[i]))
                                return false;
                        }
                    }
                }
#endregion
            }
#endregion
 
            return true;
        }
    }
}