/// <summary>
        /// Creates a converter that can convert a generic type instance that has the given types
        /// </summary>
        /// <param name="registryBuilder"></param>
        /// <param name="types"></param>
        /// <returns></returns>
        internal ITypeConverter CreateConverter(TypeRegistryBuilder registryBuilder, params Type[] types)
        {
            if (registryBuilder == null)
            {
                throw new ArgumentNullException(nameof(registryBuilder));
            }

            if (types == null)
            {
                throw new ArgumentNullException(nameof(types));
            }

            if (ConverterType.GenericTypeParameters.Length != types.Length)
            {
                throw new ArgumentException(
                          $"Generic type {ConverterType.FullName} instantiation requested {types.Length} type arguments, {ConverterType.GenericTypeParameters.Length} required",
                          nameof(types));
            }

            var uniqueTypes = types.Distinct().ToArray();

            //Look up the type meta data for each type so we can get the converter for each type
            var typesMetaData = new TypeMetaData[uniqueTypes.Length];

            for (var i = 0; i < uniqueTypes.Length; ++i)
            {
                var type = uniqueTypes[i];

                var metaData = registryBuilder.FindMetaDataByType(type);

                if (metaData == null)
                {
                    throw new InvalidOperationException($"Generic type {ConverterType.FullName} instantion is referencing type {type.FullName}, which has not been registered");
                }

                if (metaData.Converter == null)
                {
                    throw new InvalidOperationException($"Generic type {ConverterType.FullName} instantion is referencing type {type.FullName}, which has no converter associated with it");
                }

                typesMetaData[i] = metaData;
            }

            var instanceType = ConverterType.MakeGenericType(types);

            //Find a constructor that takes the unique arguments in the order that they first occurred
            var converterTypes = typesMetaData.Select(subConverter => subConverter.Converter.GetType()).ToArray();

            var constructor = instanceType.GetConstructor(converterTypes);

            if (constructor == null)
            {
                throw new InvalidOperationException($"Generic type converter {ConverterType.FullName} does not have a constructor taking each generic type converter");
            }

            var subConverters = typesMetaData.Select(subConverter => subConverter.Converter).ToArray();

            return((ITypeConverter)constructor.Invoke(subConverters));
        }
        internal TypeMetaDataBuilder(TypeRegistryBuilder registryBuilder, Type type)
        {
            _registryBuilder = registryBuilder ?? throw new ArgumentNullException(nameof(registryBuilder));
            Type             = type ?? throw new ArgumentNullException(nameof(type));

            if (type.IsGenericType && !type.IsConstructedGenericType)
            {
                throw new ArgumentException($"Type {type.FullName} is a generic type");
            }

            if (_registryBuilder.FindMetaDataByType(type) != null)
            {
                throw new ArgumentException($"Type {type.FullName} has already been registered");
            }
        }