internal void AddNullableTypeReader(Type valueType, TypeReader valueTypeReader)
        {
            var readers        = _typeReaders.GetOrAdd(typeof(Nullable <>).MakeGenericType(valueType), x => new ConcurrentDictionary <Type, TypeReader>());
            var nullableReader = NullableTypeReader.Create(valueType, valueTypeReader);

            readers[nullableReader.GetType()] = nullableReader;
        }
        /// <summary>
        ///     Initializes a new <see cref="CommandService"/> class with the provided configuration.
        /// </summary>
        /// <param name="config">The configuration class.</param>
        /// <exception cref="InvalidOperationException">
        /// The <see cref="RunMode"/> cannot be set to <see cref="RunMode.Default"/>.
        /// </exception>
        public CommandService(CommandServiceConfig config)
        {
            _caseSensitive         = config.CaseSensitiveCommands;
            _throwOnError          = config.ThrowOnError;
            _ignoreExtraArgs       = config.IgnoreExtraArgs;
            _separatorChar         = config.SeparatorChar;
            _defaultRunMode        = config.DefaultRunMode;
            _quotationMarkAliasMap = (config.QuotationMarkAliasMap ?? new Dictionary <char, char>()).ToImmutableDictionary();
            if (_defaultRunMode == RunMode.Default)
            {
                throw new InvalidOperationException("The default run mode cannot be set to Default.");
            }

            _logManager          = new LogManager(config.LogLevel);
            _logManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false);

            _cmdLogger = _logManager.CreateLogger("Command");

            _moduleLock      = new SemaphoreSlim(1, 1);
            _typedModuleDefs = new ConcurrentDictionary <Type, ModuleInfo>();
            _moduleDefs      = new HashSet <ModuleInfo>();
            _map             = new CommandMap(this);
            _typeReaders     = new ConcurrentDictionary <Type, ConcurrentDictionary <Type, TypeReader> >();

            _defaultTypeReaders = new ConcurrentDictionary <Type, TypeReader>();
            foreach (var type in PrimitiveParsers.SupportedTypes)
            {
                _defaultTypeReaders[type] = PrimitiveTypeReader.Create(type);
                _defaultTypeReaders[typeof(Nullable <>).MakeGenericType(type)] = NullableTypeReader.Create(type, _defaultTypeReaders[type]);
            }

            var tsreader = new TimeSpanTypeReader();

            _defaultTypeReaders[typeof(TimeSpan)]  = tsreader;
            _defaultTypeReaders[typeof(TimeSpan?)] = NullableTypeReader.Create(typeof(TimeSpan), tsreader);

            _defaultTypeReaders[typeof(string)] =
                new PrimitiveTypeReader <string>((string x, out string y) => { y = x; return(true); }, 0);

            var entityTypeReaders = ImmutableList.CreateBuilder <Tuple <Type, Type> >();

            entityTypeReaders.Add(new Tuple <Type, Type>(typeof(IMessage), typeof(MessageTypeReader <>)));
            entityTypeReaders.Add(new Tuple <Type, Type>(typeof(IChannel), typeof(ChannelTypeReader <>)));
            entityTypeReaders.Add(new Tuple <Type, Type>(typeof(IRole), typeof(RoleTypeReader <>)));
            entityTypeReaders.Add(new Tuple <Type, Type>(typeof(IUser), typeof(UserTypeReader <>)));
            _entityTypeReaders = entityTypeReaders.ToImmutable();
        }
        /// <summary>
        ///     Adds a custom <see cref="TypeReader" /> to this <see cref="CommandService" /> for the supplied object
        ///     type.
        ///     If <paramref name="type" /> is a <see cref="ValueType" />, a nullable <see cref="TypeReader" /> for the
        ///     value type will also be added.
        /// </summary>
        /// <param name="type">A <see cref="Type" /> instance for the type to be read.</param>
        /// <param name="reader">An instance of the <see cref="TypeReader" /> to be added.</param>
        /// <param name="replaceDefault">
        ///     Defines whether the <see cref="TypeReader"/> should replace the default one for <see cref="Type" /> if
        ///     it exists.
        /// </param>
        public void AddTypeReader(Type type, TypeReader reader, bool replaceDefault)
        {
            if (replaceDefault && HasDefaultTypeReader(type))
            {
                _defaultTypeReaders.AddOrUpdate(type, reader, (k, v) => reader);
                if (type.GetTypeInfo().IsValueType)
                {
                    var nullableType   = typeof(Nullable <>).MakeGenericType(type);
                    var nullableReader = NullableTypeReader.Create(type, reader);
                    _defaultTypeReaders.AddOrUpdate(nullableType, nullableReader, (k, v) => nullableReader);
                }
            }
            else
            {
                var readers = _typeReaders.GetOrAdd(type, x => new ConcurrentDictionary <Type, TypeReader>());
                readers[reader.GetType()] = reader;

                if (type.GetTypeInfo().IsValueType)
                {
                    AddNullableTypeReader(type, reader);
                }
            }
        }