internal static ConstructorInfo GetConstructorInfo(this Type type, params object[] parameters)
        {
            var key = type.FullName + string.Join("", parameters?.Select(x => x.GetType()));

            if (ConstructorInfos.ContainsKey(key))
            {
                return(ConstructorInfos.Get(key));
            }
#if !NETSTANDARD1_3
            return(ConstructorInfos.GetOrAdd(key, parameters == null ? type.GetConstructor(Type.EmptyTypes) : type.GetConstructor(parameters.Select(x => x.GetType()).ToArray())));
#else
            ConstructorInfo constructor = null;

            foreach (var cr in type.GetTypeInfo().DeclaredConstructors)
            {
                var index = 0;
                var args  = cr.GetParameters();
                if (args.Length == parameters.Length)
                {
                    var apply = true;
                    foreach (var pr in args)
                    {
                        var prType    = pr.ParameterType;
                        var paramType = parameters[index].GetType();

                        if (prType != paramType)
                        {
                            try
                            {
                                Convert.ChangeType(parameters[index], prType);
                            }
                            catch
                            {
                                apply = false;
                                break;
                            }
                        }
                        index++;
                    }
                    if (apply)
                    {
                        constructor = cr;
                    }
                }
            }
            return(ConstructorInfos.GetOrAdd(key, constructor));
#endif
        }
        internal static Type GetFastType(this string typeName, string assembly)
        {
            if (string.IsNullOrEmpty(assembly))
            {
                throw new Exception("AssemblyName cannot be empty");
            }

            if (!assembly.ToLower().EndsWith(".dll"))
            {
                assembly += ".dll";
            }
            var key = typeName + assembly;

            if (CachedStringTypes.ContainsKey(key))
            {
                return(CachedStringTypes.Get(key));
            }

            var type = Type.GetType($"{typeName}, {assembly.Substring(0, assembly.ToLower().IndexOf(".dll"))}");

            if (type != null)
            {
                if (!CachedAssembly.ContainsKey(assembly))
                {
                    CachedAssembly.TryAdd(assembly, type.Assembly);
                }
                return(CachedStringTypes.GetOrAdd(key, type));
            }
            else
            if (!CachedAssembly.ContainsKey(assembly))
            {
                CachedAssembly.TryAdd(assembly, Assembly.LoadFrom(assembly));
            }

            return(CachedStringTypes.GetOrAdd(key, CachedAssembly.Get(assembly).GetType(typeName, true, true)));
        }
        internal static string GetFastDeepClonerIdentifier(this object o)
        {
            if (o == null)
            {
                return(null);
            }
            var type = o.GetType();
            var p    = CachedFastDeepClonerIdentifier.ContainsKey(type) ? CachedFastDeepClonerIdentifier[type] : CachedFastDeepClonerIdentifier.GetOrAdd(type, DeepCloner.GetFastDeepClonerProperties(type).FirstOrDefault(x => x.FastDeepClonerPrimaryIdentifire));

            return(p == null ? null : type.FullName + type.Name + p.Name + p.FullName + p.GetValue(o));
        }
        internal static object Creator(this Type type, bool validateArgs = true, params object[] parameters)
        {
            try
            {
                var key         = type.FullName + string.Join("", parameters?.Select(x => x.GetType().FullName));
                var constructor = type.GetConstructorInfo(parameters ?? new object[0]);
                if (constructor == null && parameters?.Length > 0)
                {
                    constructor = type.GetConstructorInfo(new object[0]);
                }
                if (constructor != null)
                {
                    var constParam = constructor.GetParameters();
                    if (validateArgs && (parameters?.Any() ?? false))
                    {
                        for (var i = 0; i < parameters.Length; i++)
                        {
                            if (constParam.Length <= i)
                            {
                                continue;
                            }
                            if (constParam[i].ParameterType != parameters[i].GetType())
                            {
                                try
                                {
                                    parameters[i] = Convert.ChangeType(parameters[i], constParam[i].ParameterType);
                                }
                                catch
                                {
                                    // Ignore
                                }
                            }
                        }
                    }

#if NETSTANDARD2_0 || NETSTANDARD1_3 || NETSTANDARD1_5
                    if (!constParam.Any())
                    {
                        if (CachedConstructor.ContainsKey(key))
                        {
                            return(CachedConstructor[key]());
                        }
                    }
                    else if (CachedConstructorWithParameter.ContainsKey(key))
                    {
                        return(CachedConstructorWithParameter[key](parameters));
                    }

                    if (!(parameters?.Any() ?? false))
                    {
                        return(CachedConstructor.GetOrAdd(key, Expression.Lambda <Func <object> >(Expression.New(type)).Compile())());
                    }
                    else
                    {
                        // Create a single param of type object[].
                        ParameterExpression param = Expression.Parameter(typeof(object[]), "args");

                        // Pick each arg from the params array and create a typed expression of them.
                        Expression[] argsExpressions = new Expression[constParam.Length];

                        for (int i = 0; i < constParam.Length; i++)
                        {
                            Expression index            = Expression.Constant(i);
                            Type       paramType        = constParam[i].ParameterType;
                            Expression paramAccessorExp = Expression.ArrayIndex(param, index);
                            Expression paramCastExp     = Expression.Convert(paramAccessorExp, paramType);
                            argsExpressions[i] = paramCastExp;
                        }


                        return(CachedConstructorWithParameter.GetOrAdd(key, Expression.Lambda <Func <object[], object> >(Expression.New(constructor, argsExpressions), param).Compile())(parameters));
                    }
#else
                    if (!constParam.Any())
                    {
                        if (CachedDynamicMethod.ContainsKey(key))
                        {
                            return(CachedDynamicMethod[key]());
                        }
                    }
                    else if (CachedDynamicMethodWithParameters.ContainsKey(key))
                    {
                        return(CachedDynamicMethodWithParameters[key](parameters));
                    }

                    lock (CachedDynamicMethod)
                    {
                        var dynamicMethod = new System.Reflection.Emit.DynamicMethod("CreateInstance", type, (constParam.Any() ? new Type[] { typeof(object[]) } : Type.EmptyTypes), true);
                        System.Reflection.Emit.ILGenerator ilGenerator = dynamicMethod.GetILGenerator();


                        if (constructor.GetParameters().Any())
                        {
                            for (int i = 0; i < constParam.Length; i++)
                            {
                                Type paramType = constParam[i].ParameterType;
                                ilGenerator.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);    // Push array (method argument)
                                ilGenerator.Emit(System.Reflection.Emit.OpCodes.Ldc_I4, i);  // Push i
                                ilGenerator.Emit(System.Reflection.Emit.OpCodes.Ldelem_Ref); // Pop array and i and push array[i]
                                if (paramType.IsValueType)
                                {
                                    ilGenerator.Emit(System.Reflection.Emit.OpCodes.Unbox_Any, paramType); // Cast to Type t
                                }
                                else
                                {
                                    ilGenerator.Emit(System.Reflection.Emit.OpCodes.Castclass, paramType); //Cast to Type t
                                }
                            }
                        }


                        //ilGenerator.Emit(System.Reflection.Emit.OpCodes.Nop);
                        ilGenerator.Emit(System.Reflection.Emit.OpCodes.Newobj, constructor);
                        //ilGenerator.Emit(System.Reflection.Emit.OpCodes.Stloc_1); // nothing
                        ilGenerator.Emit(System.Reflection.Emit.OpCodes.Ret);

                        if (!constParam.Any())
                        {
                            return(CachedDynamicMethod.GetOrAdd(key, (ObjectActivator)dynamicMethod.CreateDelegate(typeof(ObjectActivator)))());
                        }
                        else
                        {
                            return(CachedDynamicMethodWithParameters.GetOrAdd(key, (ObjectActivatorWithParameters)dynamicMethod.CreateDelegate(typeof(ObjectActivatorWithParameters)))(parameters));
                        }
                    }
#endif
                }
                else
                {
#if !NETSTANDARD1_3
                    return(FormatterServices.GetUninitializedObject(type));
#else
                    try
                    {
                        if (CachedConstructor.ContainsKey(key))
                        {
                            return(CachedConstructor[key]());
                        }
                        return(CachedConstructor.GetOrAdd(key, Expression.Lambda <Func <object> >(Expression.New(type)).Compile())());
                    }
                    catch
                    {
                        throw new Exception("DeepClonerError: Default constructor is require for NETSTANDARD1_3 for type " + type.FullName);
                    }
#endif
                }
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        internal static ConstructorInfo GetConstructorInfo(this Type type, params object[] parameters)
        {
            IEnumerable <ConstructorInfo> constructors;
            var key = type.FullName + string.Join("", parameters?.Select(x => x.GetType()));

            if (ConstructorInfo.ContainsKey(key))
            {
                return(ConstructorInfo.Get(key));
            }

#if !NETSTANDARD1_3
            constructors = type.GetConstructors();
#else
            constructors = type.GetTypeInfo().DeclaredConstructors;
#endif
            ConstructorInfo constructor = null;
            foreach (var cr in constructors)
            {
                var index = 0;
                var args  = cr.GetParameters();
                if (args.Length == parameters.Length)
                {
                    var apply = true;
                    foreach (var pr in args)
                    {
                        var prType    = pr.ParameterType;
                        var paramType = parameters[index].GetType();

                        if (prType != paramType)
                        {
                            try
                            {
                                if ((prType.IsInternalType() && paramType.IsInternalType()))
                                {
                                    Convert.ChangeType(parameters[index], prType);
                                }
                                else
                                {
                                    if (prType.GetTypeInfo().IsInterface&& paramType.GetTypeInfo().IsAssignableFrom(prType.GetTypeInfo()))
                                    {
                                        continue;
                                    }
                                    else
                                    {
                                        apply = false;
                                        break;
                                    }
                                }
                            }
                            catch
                            {
                                apply = false;
                                break;
                            }
                        }
                        index++;
                    }
                    if (apply)
                    {
                        constructor = cr;
                    }
                }
            }

            return(ConstructorInfo.GetOrAdd(key, constructor));
        }
        internal static object ConvertToInterface(this Type interfaceType, object item)
        {
            var type = item.GetType();

            if (interfaceType.IsAssignableFrom(type))
            {
                return(item);
            }
            var props = DeepCloner.GetFastDeepClonerProperties(type);
            var args  = new SafeValueType <string, object>();

            foreach (var iProp in DeepCloner.GetFastDeepClonerProperties(interfaceType))
            {
                var p = DeepCloner.GetProperty(type, iProp.Name);
                if (p == null)
                {
                    continue;
                }
                var value = p.GetValue(item);
                if (value == null || p.PropertyType == iProp.PropertyType)
                {
                    args.Add(iProp.Name, value);
                    continue;
                }
                try
                {
                    if (iProp.PropertyType.IsInterface)
                    {
                        args.Add(iProp.Name, iProp.PropertyType.InterFaceConverter(value));
                    }
                    else
                    {
                        args.Add(iProp.Name, Convert.ChangeType(value, DeepCloner.GetProperty(interfaceType, p.Name).PropertyType));
                    }
                }
                catch (Exception e)
                {
                    var iType = iProp.PropertyType;
                    throw new Exception($"Property {p.Name} has different type then the interface which is of type {iProp.PropertyType}. \n (Convert.ChangeType) could not convert from {p.PropertyType} to {iType}. \n Orginal Exception: {e.Message}");
                }
            }

            var key         = $"{(type.IsAnonymousType() ? string.Join(" | ", props.Select(x => x.FullName).ToArray()) : type.FullName)} | {interfaceType.FullName}";
            var newtype     = CachedConvertedObjectToInterface.ContainsKey(key) ? CachedConvertedObjectToInterface[key] : CachedConvertedObjectToInterface.GetOrAdd(key, FastDeepCloner.ConvertToInterfaceTypeGenerator.Convert(interfaceType, type));
            var returnValue = Creator(newtype, false, args.Values.ToArray());
            var constructor = GetConstructorInfo(newtype, args.Values.ToArray());

            if (constructor == null)
            {
                foreach (var p in args)
                {
                    DeepCloner.GetProperty(newtype, p.Key).SetValue(returnValue, args[p.Key]);
                }
            }
            return(returnValue);
        }