public static ScoredConstructor GetBestConstructor(Type type, IEnumerable <Type> argumentTypes)
        {
            var context = new ConstructorDiscoveryContext(type, argumentTypes);
            var scored  = GetConstructorsByScore(context);

            return(scored.Where(sc => sc.Score >= 0).OrderByDescending(sc => sc.Score).FirstOrDefault());
        }
        private static List <ScoredConstructor> GetConstructorsByScore(ConstructorDiscoveryContext context)
        {
            var constructors = context.TargetType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
            var scoredList   = new List <ScoredConstructor>();

            foreach (var constructor in constructors)
            {
                ScoredConstructor scored = ScoreConstructor(constructor, context);
                scoredList.Add(scored);
            }
            return(scoredList);
        }
        private static ScoredConstructor ScoreConstructor(ConstructorInfo constructor, ConstructorDiscoveryContext context)
        {
            if (constructor.IsPrivate)
            {
                return(ScoredConstructor.Unusable(constructor));
            }

            // parameters are the defined formal parameters of the constructor
            // arguments are the values and object references provided by the caller
            var parameters = constructor.GetParameters();

            if (parameters.Length == 0)
            {
                return(ScoredConstructor.Parameterless(constructor));
            }

            int         score         = 0;
            List <bool> usedArguments = context.ArgumentTypes.Select(t => false).ToList();
            var         argumentMap   = new Dictionary <int, int>();

            for (int paramIdx = 0; paramIdx < parameters.Length; paramIdx++)
            {
                var parameter     = parameters[paramIdx];
                int foundScore    = -1;
                var parameterType = parameter.ParameterType;
                for (int argIdx = 0; argIdx < context.ArgumentTypes.Count; argIdx++)
                {
                    if (usedArguments[argIdx])
                    {
                        continue;
                    }
                    var argType = context.ArgumentTypes[argIdx];
                    foundScore = ScoreTypeMatch(argType, parameterType);
                    if (foundScore >= 0)
                    {
                        usedArguments[argIdx] = true;
                        argumentMap.Add(paramIdx, argIdx);
                        score += foundScore;
                        break;
                    }
                }
                if (foundScore < 0)
                {
                    return(ScoredConstructor.Unusable(constructor));
                }
            }

            return(new ScoredConstructor(score, constructor, argumentMap));
        }