/// <summary>
        /// Creates a parsing delegate for a specified type. Uses <paramref name="converter"/> if it's compatible; otherwise uses the first compatible match from <see cref="OptionArgumentValueConverters"/>; otherwise attempts to call a <c>TryParse</c> method via reflection.
        /// If no parser can be found, throws <see cref="InvalidOperationException"/> eagerly (i.e., before parsing user input).
        /// </summary>
        /// <param name="type">The type to convert to. May not be <c>null</c>.</param>
        /// <param name="converter">The custom converter, if any. May be <c>null</c>.</param>
        private Func <string, object> GetParser(Type type, IOptionArgumentValueConverter converter)
        {
            var result = TryGetExactParser(type, converter);

            if (result != null)
            {
                return(result);
            }

            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                result = TryGetExactParser(type.GenericTypeArguments[0], converter);
                if (result != null)
                {
                    return(result);
                }
            }

            throw new InvalidOperationException($"Cannot determine how to parse option argument value of type {type.Name}.");
        }
        /// <summary>
        /// Creates a parsing delegate for a specified type. Uses <paramref name="converter"/> if it's compatible; otherwise uses the first compatible match from <see cref="OptionArgumentValueConverters"/>; otherwise attempts to call a <c>TryParse</c> method via reflection.
        /// If no parser can be found, returns <c>null</c>.
        /// </summary>
        /// <param name="type">The type to convert to. May not be <c>null</c>.</param>
        /// <param name="converter">The custom converter, if any. May be <c>null</c>.</param>
        private Func <string, object> TryGetExactParser(Type type, IOptionArgumentValueConverter converter)
        {
            if (converter != null && converter.CanConvert(type))
            {
                return(MakeAction(type, converter));
            }
            var selected = OptionArgumentValueConverters.FirstOrDefault(x => x.CanConvert(type));

            if (selected != null)
            {
                return(MakeAction(type, selected));
            }
            if (type == typeof(string))
            {
                return(Identity);
            }

            var tryParse = type.GetMethod(
                "TryParse",
                BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public,
                null,
                new[] { typeof(string), type.MakeByRefType() },
                null);

            if (tryParse != null && tryParse.ReturnType == typeof(bool))
            {
                // type.TryParse(value, out result)
                return(value =>
                {
                    var arguments = new object[] { value, null };
                    var result = (bool)tryParse.Invoke(null, arguments);
                    return result ? arguments[1] : null;
                });
            }

            if (type.IsEnum)
            {
                // Enum.TryParse<type>(value, true, out result)
                var genericEnumTryParse = typeof(Enum)
                                          .GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public)
                                          .Where(x => x.Name == "TryParse" && x.IsGenericMethod && x.ContainsGenericParameters)
                                          .Select(x => new { Method = x, Parameters = x.GetParameters(), Arguments = x.GetGenericArguments() })
                                          .Where(x => x.Arguments.Length == 1 &&
                                                 x.Parameters.Length == 3 &&
                                                 x.Parameters[0].ParameterType == typeof(string) &&
                                                 x.Parameters[1].ParameterType == typeof(bool) &&
                                                 x.Parameters[2].ParameterType == x.Arguments[0].MakeByRefType())
                                          .Select(x => x.Method)
                                          .FirstOrDefault();
                if (genericEnumTryParse != null)
                {
                    var enumTryParse = genericEnumTryParse.MakeGenericMethod(type);
                    return(value =>
                    {
                        var arguments = new object[] { value, true, null };
                        var result = (bool)enumTryParse.Invoke(null, arguments);
                        return result ? arguments[2] : null;
                    });
                }
            }

            return(null);

            Func <string, object> MakeAction(Type t, IOptionArgumentValueConverter parser) => value => parser.TryConvert(t, value);
        }