diff --git a/CliFx/ApplicationMetadata.cs b/CliFx/ApplicationMetadata.cs
index 5674724..0f778c1 100644
--- a/CliFx/ApplicationMetadata.cs
+++ b/CliFx/ApplicationMetadata.cs
@@ -28,7 +28,11 @@
///
/// Initializes an instance of .
///
- public ApplicationMetadata(string title, string executableName, string versionText, string? description)
+ public ApplicationMetadata(
+ string title,
+ string executableName,
+ string versionText,
+ string? description)
{
Title = title;
ExecutableName = executableName;
diff --git a/CliFx/Domain/CommandArgumentSchema.cs b/CliFx/Domain/CommandArgumentSchema.cs
index c2300e7..d9fab28 100644
--- a/CliFx/Domain/CommandArgumentSchema.cs
+++ b/CliFx/Domain/CommandArgumentSchema.cs
@@ -28,44 +28,52 @@ namespace CliFx.Domain
private Type? TryGetEnumerableArgumentUnderlyingType() =>
Property != null && Property.PropertyType != typeof(string)
- ? Property.PropertyType.GetEnumerableUnderlyingType()
+ ? Property.PropertyType.TryGetEnumerableUnderlyingType()
: null;
private object? ConvertScalar(string? value, Type targetType)
{
try
{
- // Custom conversion
+ // Custom conversion (always takes highest priority)
if (ConverterType != null)
return ConverterType.CreateInstance().ConvertFrom(value!);
- // Primitive
+ // No conversion necessary
+ if (targetType == typeof(object) || targetType == typeof(string))
+ return value;
+
+ // Bool conversion (special case)
+ if (targetType == typeof(bool))
+ return string.IsNullOrWhiteSpace(value) || bool.Parse(value);
+
+ // Primitive conversion
var primitiveConverter = PrimitiveConverters.GetValueOrDefault(targetType);
- if (primitiveConverter != null)
+ if (primitiveConverter != null && !string.IsNullOrWhiteSpace(value))
return primitiveConverter(value);
- // Enum
- if (targetType.IsEnum)
+ // Enum conversion
+ if (targetType.IsEnum && !string.IsNullOrWhiteSpace(value))
return Enum.Parse(targetType, value, true);
- // Nullable
- var nullableUnderlyingType = targetType.GetNullableUnderlyingType();
+ // Nullable conversion
+ var nullableUnderlyingType = targetType.TryGetNullableUnderlyingType();
if (nullableUnderlyingType != null)
return !string.IsNullOrWhiteSpace(value)
? ConvertScalar(value, nullableUnderlyingType)
: null;
- // String-constructible
+ // String-constructible conversion
var stringConstructor = targetType.GetConstructor(new[] {typeof(string)});
if (stringConstructor != null)
return stringConstructor.Invoke(new object[] {value!});
- // String-parseable (with format provider)
+ // String-parseable conversion (with format provider)
var parseMethodWithFormatProvider = targetType.TryGetStaticParseMethod(true);
if (parseMethodWithFormatProvider != null)
return parseMethodWithFormatProvider.Invoke(null, new object[] {value!, FormatProvider});
- // String-parseable (without format provider)
+ // String-parseable conversion (without format provider)
var parseMethod = targetType.TryGetStaticParseMethod();
if (parseMethod != null)
return parseMethod.Invoke(null, new object[] {value!});
@@ -78,7 +86,10 @@ namespace CliFx.Domain
throw CliFxException.CannotConvertToType(this, value, targetType);
}
- private object ConvertNonScalar(IReadOnlyList values, Type targetEnumerableType, Type targetElementType)
+ private object ConvertNonScalar(
+ IReadOnlyList values,
+ Type targetEnumerableType,
+ Type targetElementType)
{
var array = values
.Select(v => ConvertScalar(v, targetElementType))
@@ -133,7 +144,7 @@ namespace CliFx.Domain
return Array.Empty();
var underlyingType =
- Property.PropertyType.GetNullableUnderlyingType() ??
+ Property.PropertyType.TryGetNullableUnderlyingType() ??
Property.PropertyType;
// Enum
@@ -148,12 +159,9 @@ namespace CliFx.Domain
{
private static readonly IFormatProvider FormatProvider = CultureInfo.InvariantCulture;
- private static readonly IReadOnlyDictionary> PrimitiveConverters =
- new Dictionary>
+ private static readonly IReadOnlyDictionary> PrimitiveConverters =
+ new Dictionary>
{
- [typeof(object)] = v => v,
- [typeof(string)] = v => v,
- [typeof(bool)] = v => string.IsNullOrWhiteSpace(v) || bool.Parse(v),
[typeof(char)] = v => v.Single(),
[typeof(sbyte)] = v => sbyte.Parse(v, FormatProvider),
[typeof(byte)] = v => byte.Parse(v, FormatProvider),
diff --git a/CliFx/Domain/HelpTextWriter.cs b/CliFx/Domain/HelpTextWriter.cs
index f2b452b..5a05b95 100644
--- a/CliFx/Domain/HelpTextWriter.cs
+++ b/CliFx/Domain/HelpTextWriter.cs
@@ -385,7 +385,7 @@ namespace CliFx.Domain
// Enumerable
if (!(defaultValue is string) && defaultValue is IEnumerable defaultValues)
{
- var elementType = defaultValues.GetType().GetEnumerableUnderlyingType() ?? typeof(object);
+ var elementType = defaultValues.GetType().TryGetEnumerableUnderlyingType() ?? typeof(object);
// If the ToString() method is not overriden, the default value can't be formatted nicely
if (!elementType.IsToStringOverriden())
diff --git a/CliFx/Internal/Extensions/TypeExtensions.cs b/CliFx/Internal/Extensions/TypeExtensions.cs
index 6a05cdb..4c93a90 100644
--- a/CliFx/Internal/Extensions/TypeExtensions.cs
+++ b/CliFx/Internal/Extensions/TypeExtensions.cs
@@ -14,9 +14,9 @@ namespace CliFx.Internal.Extensions
public static bool Implements(this Type type, Type interfaceType) => type.GetInterfaces().Contains(interfaceType);
- public static Type? GetNullableUnderlyingType(this Type type) => Nullable.GetUnderlyingType(type);
+ public static Type? TryGetNullableUnderlyingType(this Type type) => Nullable.GetUnderlyingType(type);
- public static Type? GetEnumerableUnderlyingType(this Type type)
+ public static Type? TryGetEnumerableUnderlyingType(this Type type)
{
if (type.IsPrimitive)
return null;
@@ -29,7 +29,7 @@ namespace CliFx.Internal.Extensions
return type
.GetInterfaces()
- .Select(GetEnumerableUnderlyingType)
+ .Select(TryGetEnumerableUnderlyingType)
.Where(t => t != null)
.OrderByDescending(t => t != typeof(object)) // prioritize more specific types
.FirstOrDefault();
diff --git a/CliFx/Internal/Polyfills.cs b/CliFx/Internal/Polyfills.cs
index 10f6987..daa3965 100644
--- a/CliFx/Internal/Polyfills.cs
+++ b/CliFx/Internal/Polyfills.cs
@@ -34,7 +34,7 @@ namespace System.Collections.Generic
}
public static TValue GetValueOrDefault(this IReadOnlyDictionary dic, TKey key) =>
- dic.TryGetValue(key, out var result) ? result! : default!;
+ dic.TryGetValue(key!, out var result) ? result! : default!;
}
}