/// <summary>
        /// Returns an instance of this <paramref name="type"/>.
        /// </summary>
        /// <param name="type">The type an instance of which should be created.</param>
        /// <param name="arguments">The arguments to pass to the type constructor.</param>
        /// <returns>An instance of this given <paramref name="type"/>.</returns>
        /// <exception cref="NotSupportedException">If failed to find a suiting constructor.</exception>
        public static object GetInstance(this Type type, params object[] arguments)
        {
            var factoryKey  = new TypeFactoryKey(type, arguments);
            var typeFactory = _factoriesByType.GetOrAdd(factoryKey, CreateObjectFactory);

            return(typeFactory.Invoke(arguments));
        }
        private static Expression GetInstanceCreation(
            TypeFactoryKey key,
            Expression lambdaParameters)
        {
            var argumentTypes = key.ArgumentTypes;
            var argumentCount = argumentTypes.Length;

            if (argumentCount == 0)
            {
                return(Expression.New(key.Type));
            }

            // The constructor which matches the given argument types:
            var instanceTypeCtor = GetMatchingConstructor(key, argumentTypes);

            // A set of Expressions representing the parameters to pass
            // to the constructor:
            var ctorArguments = new Expression[argumentCount];

            for (var i = 0; i < argumentCount; ++i)
            {
                var argumentType = argumentTypes[i];

                // Access the approriate Lambda parameter by index:
                var lambdaParameter = Expression
                                      .ArrayAccess(lambdaParameters, Expression.Constant(i));

                // Convert the lambda parameter to the constructor
                // parameter type if necessary:
                ctorArguments[i] = argumentType == typeof(object)
                    ? (Expression)lambdaParameter
                    : Expression.Convert(lambdaParameter, argumentType);
            }

            // An Expression representing the constructor call,
            // passing in the constructor parameters:
            return(Expression.New(instanceTypeCtor, ctorArguments));
        }
        private static Func <object[], object> CreateObjectFactory(TypeFactoryKey key)
        {
            // An Expression representing the parameter to pass
            // to the Func:
            var lambdaParameters = Expression.Parameter(typeof(object[]), "params");

            // Get an Expression representing the 'new' constructor call:
            var instanceCreation = GetInstanceCreation(key, lambdaParameters);

            if (key.Type.IsValueType)
            {
                // A value type needs additional boxing:
                instanceCreation = Expression
                                   .Convert(instanceCreation, typeof(object));
            }

            // Compile the Expression into a Func which takes an
            // object argument array and returns the constructed object:
            var instanceCreationLambda = Expression
                                         .Lambda <Func <object[], object> >(instanceCreation, lambdaParameters);

            return(instanceCreationLambda.Compile());
        }
        private static ConstructorInfo GetMatchingConstructor(TypeFactoryKey key, Type[] argumentTypes)
        {
            if (!key.HasNullArgumentTypes)
            {
                return(key.Type.GetConstructor(
                           Public | Instance,
                           Type.DefaultBinder,
                           CallingConventions.HasThis,
                           argumentTypes,
                           Array.Empty <ParameterModifier>()) ??
                       throw new NotSupportedException("Failed to find a matching constructor"));
            }

            var constructors  = key.Type.GetConstructors(Public | Instance);
            var matchingCtor  = default(ConstructorInfo);
            var parameters    = default(ParameterInfo[]);
            var argumentCount = argumentTypes.Length;

            for (int i = 0, l = constructors.Length; i < l; ++i)
            {
                var constructor = constructors[i];
                parameters = constructor.GetParameters();

                if (parameters.Length != argumentCount)
                {
                    continue;
                }

                for (var j = 0; j < argumentCount; ++j)
                {
                    var argumentType = argumentTypes[j];

                    if ((argumentType != null) &&
                        !parameters[j].ParameterType.IsAssignableFrom(argumentType))
                    {
                        goto NextConstructor;
                    }
                }

                if (matchingCtor != null)
                {
                    throw new NotSupportedException(
                              "Failed to find a single matching constructor due to null arguments");
                }

                matchingCtor = constructor;

                NextConstructor :;
            }

            if (matchingCtor == null)
            {
                throw new NotSupportedException(
                          "Failed to find a matching constructor due to null arguments");
            }

            for (var j = 0; j < argumentCount; ++j)
            {
                if (argumentTypes[j] == null)
                {
                    argumentTypes[j] = parameters[j].ParameterType;
                }
            }

            return(matchingCtor);
        }