/// <summary>
        /// Prepares a Lua script with named parameters to be run against any Redis instance.
        /// </summary>
        /// <param name="script">The script to prepare.</param>
        public static LuaScript Prepare(string script)
        {
            LuaScript ret;

            if (!Cache.TryGetValue(script, out WeakReference weakRef) || (ret = (LuaScript)weakRef.Target) == null)
            {
                ret           = ScriptParameterMapper.PrepareScript(script);
                Cache[script] = new WeakReference(ret);
            }

            return(ret);
        }
        internal void ExtractParameters(object ps, RedisKey?keyPrefix, out RedisKey[] keys, out RedisValue[] args)
        {
            if (HasArguments)
            {
                if (ps == null)
                {
                    throw new ArgumentNullException(nameof(ps), "Script requires parameters");
                }

                var psType = ps.GetType();
                var mapper = (Func <object, RedisKey?, ScriptParameterMapper.ScriptParameters>)ParameterMappers[psType];
                if (ps != null && mapper == null)
                {
                    lock (ParameterMappers)
                    {
                        mapper = (Func <object, RedisKey?, ScriptParameterMapper.ScriptParameters>)ParameterMappers[psType];
                        if (mapper == null)
                        {
                            string missingMember;
                            string badMemberType;
                            if (!ScriptParameterMapper.IsValidParameterHash(psType, this, out missingMember, out badMemberType))
                            {
                                if (missingMember != null)
                                {
                                    throw new ArgumentException("ps", "Expected [" + missingMember + "] to be a field or gettable property on [" + psType.FullName + "]");
                                }

                                throw new ArgumentException("ps", "Expected [" + badMemberType + "] on [" + psType.FullName + "] to be convertable to a RedisValue");
                            }

                            ParameterMappers[psType] = mapper = ScriptParameterMapper.GetParameterExtractor(psType, this);
                        }
                    }
                }

                var mapped = mapper(ps, keyPrefix);
                keys = mapped.Keys;
                args = mapped.Arguments;
            }
            else
            {
                keys = null;
                args = null;
            }
        }