#nullable enable using System; using System.Collections.Concurrent; using System.ComponentModel; using System.Linq.Expressions; using System.Reflection; namespace Swan { /// /// Provides a standard way to convert strings to different types. /// public static class FromString { // It doesn't matter which converter we get here: ConvertFromInvariantString is not virtual. private static readonly MethodInfo ConvertFromInvariantStringMethod = new Func(TypeDescriptor.GetConverter(typeof(Int32)).ConvertFromInvariantString).Method; private static readonly MethodInfo? TryConvertToInternalMethod = typeof(FromString).GetMethod(nameof(TryConvertToInternal), BindingFlags.Static | BindingFlags.NonPublic); private static readonly MethodInfo? ConvertToInternalMethod = typeof(FromString).GetMethod(nameof(ConvertToInternal), BindingFlags.Static | BindingFlags.NonPublic); private static readonly ConcurrentDictionary> GenericTryConvertToMethods = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> GenericConvertToMethods = new ConcurrentDictionary>(); /// /// Determines whether a string can be converted to the specified type. /// /// The type resulting from the conversion. /// if the conversion is possible; /// otherwise, . /// is . public static Boolean CanConvertTo(Type type) => TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(String)); /// /// Determines whether a string can be converted to the specified type. /// /// The type resulting from the conversion. /// if the conversion is possible; /// otherwise, . public static Boolean CanConvertTo() => TypeDescriptor.GetConverter(typeof(TResult)).CanConvertFrom(typeof(String)); /// /// Attempts to convert a string to the specified type. /// /// The type resulting from the conversion. /// The string to convert. /// When this method returns , /// the result of the conversion. This parameter is passed uninitialized. /// if the conversion is successful; /// otherwise, . /// is . public static Boolean TryConvertTo(Type type, String str, out Object? result) { TypeConverter converter = TypeDescriptor.GetConverter(type); if(!converter.CanConvertFrom(typeof(String))) { result = null; return false; } try { result = converter.ConvertFromInvariantString(str); return true; } catch(Exception e) when(!e.IsCriticalException()) { result = null; return false; } } /// /// Attempts to convert a string to the specified type. /// /// The type resulting from the conversion. /// The string to convert. /// When this method returns , /// the result of the conversion. This parameter is passed uninitialized. /// if the conversion is successful; /// otherwise, . public static Boolean TryConvertTo(String str, out TResult result) where TResult : notnull { TypeConverter converter = TypeDescriptor.GetConverter(typeof(TResult)); if(!converter.CanConvertFrom(typeof(String))) { result = default!; return false; } try { result = (TResult)converter.ConvertFromInvariantString(str); return true; } catch(Exception e) when(!e.IsCriticalException()) { result = default!; return false; } } /// /// Converts a string to the specified type. /// /// The type resulting from the conversion. /// The string to convert. /// An instance of . /// is . /// The conversion was not successful. public static Object ConvertTo(Type type, String str) { if(type == null) { throw new ArgumentNullException(nameof(type)); } try { return TypeDescriptor.GetConverter(type).ConvertFromInvariantString(str); } catch(Exception e) when(!e.IsCriticalException()) { throw new StringConversionException(type, e); } } /// /// Converts a string to the specified type. /// /// The type resulting from the conversion. /// The string to convert. /// An instance of . /// /// The conversion was not successful. /// public static TResult ConvertTo(String str) { try { return (TResult)TypeDescriptor.GetConverter(typeof(TResult)).ConvertFromInvariantString(str); } catch(Exception e) when(!e.IsCriticalException()) { throw new StringConversionException(typeof(TResult), e); } } /// /// Attempts to convert an array of strings to an array of the specified type. /// /// The type resulting from the conversion of each /// element of . /// The array to convert. /// When this method returns , /// the result of the conversion. This parameter is passed uninitialized. /// if the conversion is successful; /// otherwise, . /// is . public static Boolean TryConvertTo(Type type, String[] strings, out Object? result) { if(strings == null) { result = null; return false; } Func method = GenericTryConvertToMethods.GetOrAdd(type, BuildNonGenericTryConvertLambda); (Boolean success, Object methodResult) = method(strings); result = methodResult; return success; } /// /// Attempts to convert an array of strings to an array of the specified type. /// /// The type resulting from the conversion of each /// element of . /// The array to convert. /// When this method returns , /// the result of the conversion. This parameter is passed uninitialized. /// if the conversion is successful; /// otherwise, . public static Boolean TryConvertTo(String[] strings, out TResult[]? result) { if(strings == null) { result = null; return false; } TypeConverter converter = TypeDescriptor.GetConverter(typeof(TResult)); if(!converter.CanConvertFrom(typeof(String))) { result = null; return false; } try { result = new TResult[strings.Length]; Int32 i = 0; foreach(String str in strings) { result[i++] = (TResult)converter.ConvertFromInvariantString(str); } return true; } catch(Exception e) when(!e.IsCriticalException()) { result = null; return false; } } /// /// Converts an array of strings to an array of the specified type. /// /// The type resulting from the conversion of each /// element of . /// The array to convert. /// An array of . /// is . /// The conversion of at least one /// of the elements of was not successful. public static Object? ConvertTo(Type type, String[] strings) { if(strings == null) { return null; } Func method = GenericConvertToMethods.GetOrAdd(type, BuildNonGenericConvertLambda); return method(strings); } /// /// Converts an array of strings to an array of the specified type. /// /// The type resulting from the conversion of each /// element of . /// The array to convert. /// An array of . /// The conversion of at least one /// of the elements of was not successful. public static TResult[]? ConvertTo(String[] strings) { if(strings == null) { return null; } TypeConverter converter = TypeDescriptor.GetConverter(typeof(TResult)); TResult[] result = new TResult[strings.Length]; Int32 i = 0; try { foreach(String str in strings) { result[i++] = (TResult)converter.ConvertFromInvariantString(str); } } catch(Exception e) when(!e.IsCriticalException()) { throw new StringConversionException(typeof(TResult), e); } return result; } /// /// Converts a expression, if the type can be converted to string, to a new expression including /// the conversion to string. /// /// The type. /// The string. /// A new expression where the previous expression is converted to string. public static Expression? ConvertExpressionTo(Type type, Expression str) { TypeConverter converter = TypeDescriptor.GetConverter(type); return converter.CanConvertFrom(typeof(String)) ? Expression.Convert( Expression.Call(Expression.Constant(converter), ConvertFromInvariantStringMethod, str), type) : null; } private static Func BuildNonGenericTryConvertLambda(Type type) { MethodInfo? methodInfo = TryConvertToInternalMethod?.MakeGenericMethod(type); ParameterExpression parameter = Expression.Parameter(typeof(String[])); MethodCallExpression body = Expression.Call(methodInfo, parameter); Expression> lambda = Expression.Lambda>(body, parameter); return lambda.Compile(); } private static (Boolean Success, Object? Result) TryConvertToInternal(String[] strings) { TypeConverter converter = TypeDescriptor.GetConverter(typeof(TResult)); if(!converter.CanConvertFrom(typeof(String))) { return (false, null); } TResult[] result = new TResult[strings.Length]; Int32 i = 0; try { foreach(String str in strings) { result[i++] = (TResult)converter.ConvertFromInvariantString(str); } return (true, result); } catch(Exception e) when(!e.IsCriticalException()) { return (false, null); } } private static Func BuildNonGenericConvertLambda(Type type) { MethodInfo? methodInfo = ConvertToInternalMethod?.MakeGenericMethod(type); ParameterExpression parameter = Expression.Parameter(typeof(String[])); MethodCallExpression body = Expression.Call(methodInfo, parameter); Expression> lambda = Expression.Lambda>(body, parameter); return lambda.Compile(); } private static Object ConvertToInternal(String[] strings) { TypeConverter converter = TypeDescriptor.GetConverter(typeof(TResult)); TResult[] result = new TResult[strings.Length]; Int32 i = 0; try { foreach(String str in strings) { result[i++] = (TResult)converter.ConvertFromInvariantString(str); } return result; } catch(Exception e) when(!e.IsCriticalException()) { throw new StringConversionException(typeof(TResult), e); } } } }