/// <summary>
        /// Writes the field to the CSV file.
        /// When all fields are written for a record,
        /// <see cref="IWriter.NextRecord" /> must be called
        /// to complete writing of the current record.
        /// </summary>
        /// <typeparam name="T">The type of the field.</typeparam>
        /// <param name="field">The field to write.</param>
        /// <param name="converter">The converter used to convert the field into a string.</param>
        public virtual void WriteField <T>(T field, ITypeConverter converter)
        {
            var type = field == null ? typeof(string) : field.GetType();

            context.ReusableMemberMapData.TypeConverter = converter;
            //if( !context.TypeConverterOptionsCache.TryGetValue( type, out TypeConverterOptions typeConverterOptions ) )
            TypeConverterOptions typeConverterOptions = CSharp6Extension.TryGetValue <Type, TypeConverterOptions>(context.TypeConverterOptionsCache, type);

            if (typeConverterOptions == default(TypeConverterOptions))
            {
                typeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions {
                    CultureInfo = context.WriterConfiguration.CultureInfo
                }, context.WriterConfiguration.TypeConverterOptionsCache.GetOptions(type));
                context.TypeConverterOptionsCache.Add(type, typeConverterOptions);
            }

            context.ReusableMemberMapData.TypeConverterOptions = typeConverterOptions;

            var fieldString = converter.ConvertToString(field, this, context.ReusableMemberMapData);

            WriteConvertedField(fieldString);
        }
        /// <summary>
        /// Gets the delegate to write the given record.
        /// If the delegate doesn't exist, one will be created and cached.
        /// </summary>
        /// <typeparam name="T">The record type.</typeparam>
        /// <param name="record">The record.</param>
        protected Action <T> GetWriteDelegate <T>(T record)
        {
            var type        = typeof(T);
            var typeKeyName = type.AssemblyQualifiedName;

            if (type == typeof(object))
            {
                type         = record.GetType();
                typeKeyName += $"|{type.AssemblyQualifiedName}";
            }

            int typeKey = typeKeyName.GetHashCode();

            Delegate action = CSharp6Extension.TryGetValue <int, Delegate>(Writer.Context.TypeActions, typeKey);

            //if( !Writer.Context.TypeActions.TryGetValue( typeKey, out Delegate action ) )
            if (action == default(Delegate))
            {
                Writer.Context.TypeActions[typeKey] = action = CreateWriteDelegate(record);
            }

            return((Action <T>)action);
        }
        /// <summary>
        /// Gets the converter for the given <see cref="System.Type"/>.
        /// </summary>
        /// <param name="type">The type to get the converter for.</param>
        /// <returns>The <see cref="ITypeConverter"/> for the given <see cref="System.Type"/>.</returns>
        public ITypeConverter GetConverter(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            ITypeConverter typeConverter = CSharp6Extension.TryGetValue <Type, ITypeConverter>(typeConverters, type);

            //if( typeConverters.TryGetValue( type, out ITypeConverter typeConverter ) )
            if (typeConverter != default(ITypeConverter))
            {
                return(typeConverter);
            }

            if (typeof(Enum).IsAssignableFrom(type))
            {
                AddConverter(type, new EnumConverter(type));
                return(GetConverter(type));
            }

            if (type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                AddConverter(type, new NullableConverter(type, this));
                return(GetConverter(type));
            }

            if (type.IsArray)
            {
                AddConverter(type, new ArrayConverter());
                return(GetConverter(type));
            }

            if (type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(Dictionary <,>))
            {
                AddConverter(type, new IDictionaryGenericConverter());
                return(GetConverter(type));
            }

            if (type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(IDictionary <,>))
            {
                AddConverter(type, new IDictionaryGenericConverter());
                return(GetConverter(type));
            }

            if (type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(List <>))
            {
                AddConverter(type, new CollectionGenericConverter());
                return(GetConverter(type));
            }

            if (type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(Collection <>))
            {
                AddConverter(type, new CollectionGenericConverter());
                return(GetConverter(type));
            }

            if (type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(IList <>))
            {
                AddConverter(type, new IEnumerableGenericConverter());
                return(GetConverter(type));
            }

            if (type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(ICollection <>))
            {
                AddConverter(type, new IEnumerableGenericConverter());
                return(GetConverter(type));
            }

            if (type.GetTypeInfo().IsGenericType&& type.GetGenericTypeDefinition() == typeof(IEnumerable <>))
            {
                AddConverter(type, new IEnumerableGenericConverter());
                return(GetConverter(type));
            }

            // A specific IEnumerable converter doesn't exist.
            if (typeof(IEnumerable).IsAssignableFrom(type))
            {
                return(new EnumerableConverter());
            }

            return(new DefaultTypeConverter());
        }