/// <summary>
        /// Registers a factory for a scheme, overwriting any existing factory for that scheme.
        /// </summary>
        /// <typeparam name="T">The type of the URI.</typeparam>
        /// <param name="scheme">The scheme. This must be a valid scheme, as defined by <see cref="Utility.IsValidScheme"/>.</param>
        /// <param name="factory">The factory method for URIs following this scheme. This method must perform any scheme-specific validation.</param>
        public static void RegisterSchemeFactory <T>(string scheme, Utility.DelegateFactory <T> factory)
            where T : IUniformResourceIdentifier
        {
            if (scheme == null || !Utility.IsValidScheme(scheme))
            {
                throw new ArgumentException("Invalid scheme " + scheme, nameof(scheme));
            }

            lock (_factories)
            {
                _factories[scheme] = (userInfo, host, port, pathSegments, query, fragment) => factory(userInfo, host, port, pathSegments, query, fragment);
            }
        }
        /// <summary>
        /// Constructs a new URI instance.
        /// </summary>
        /// <param name="scheme">The scheme. This is converted to lowercase. Must be a valid scheme as defined by <see cref="Utility.IsValidScheme"/>.</param>
        /// <param name="userInfo">The user information portion of the authority, if any. This may be <c>null</c> to indicate no user info, or the empty string to indicate empty user info.</param>
        /// <param name="host">The host name portion of the authority, if any. This is converted to lowercase. This may be <c>null</c> to indicate no host name, or the empty string to indicate an empty host name.</param>
        /// <param name="port">The port portion of the authority, if any. This may be <c>null</c> to indicate no port, or the empty string to indicate an empty port. This must be <c>null</c>, the empty string, or a valid port as defined by <see cref="Utility.IsValidPort"/>.</param>
        /// <param name="pathSegments">The path segments. Dot segments are normalized. May not be <c>null</c>, neither may any element be <c>null</c>.</param>
        /// <param name="query">The query. This may be <c>null</c> to indicate no query, or the empty string to indicate an empty query.</param>
        /// <param name="fragment">The fragment. This may be <c>null</c> to indicate no fragment, or the empty string to indicate an empty fragment.</param>
        public GenericUniformResourceIdentifier(string scheme, string?userInfo, string?host, string?port, IEnumerable <string> pathSegments, string?query, string?fragment)
        {
            _ = scheme ?? throw new ArgumentNullException(nameof(scheme));
            if (!Utility.IsValidScheme(scheme))
            {
                throw new ArgumentException("Invalid scheme " + scheme, nameof(scheme));
            }
            Scheme        = scheme.ToLowerInvariant();
            UserInfo      = userInfo;
            _host         = new NormalizedHost(host);
            _port         = new NormalizedPort(port);
            _pathSegments = new NormalizedPathSegments(pathSegments, UserInfo, _host.Value, _port.Value);
            Query         = query;
            Fragment      = fragment;

            _factory = CreateFactory(scheme);
        }