2019-12-08 19:54:52 +01:00
|
|
|
|
#nullable enable
|
|
|
|
|
using System;
|
2019-12-04 18:57:18 +01:00
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
2019-12-08 19:54:52 +01:00
|
|
|
|
namespace Swan {
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides a standard way to convert strings to different types.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static class FromString {
|
|
|
|
|
// It doesn't matter which converter we get here: ConvertFromInvariantString is not virtual.
|
|
|
|
|
private static readonly MethodInfo ConvertFromInvariantStringMethod = new Func<String, Object>(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<Type, Func<String[], (Boolean Success, Object Result)>> GenericTryConvertToMethods = new ConcurrentDictionary<Type, Func<String[], (Boolean Success, Object Result)>>();
|
|
|
|
|
|
|
|
|
|
private static readonly ConcurrentDictionary<Type, Func<String[], Object>> GenericConvertToMethods = new ConcurrentDictionary<Type, Func<String[], Object>>();
|
|
|
|
|
|
2019-12-04 18:57:18 +01:00
|
|
|
|
/// <summary>
|
2019-12-08 19:54:52 +01:00
|
|
|
|
/// Determines whether a string can be converted to the specified type.
|
2019-12-04 18:57:18 +01:00
|
|
|
|
/// </summary>
|
2019-12-08 19:54:52 +01:00
|
|
|
|
/// <param name="type">The type resulting from the conversion.</param>
|
|
|
|
|
/// <returns><see langword="true" /> if the conversion is possible;
|
|
|
|
|
/// otherwise, <see langword="false" />.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
|
|
|
|
public static Boolean CanConvertTo(Type type) => TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(String));
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines whether a string can be converted to the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="TResult">The type resulting from the conversion.</typeparam>
|
|
|
|
|
/// <returns><see langword="true" /> if the conversion is possible;
|
|
|
|
|
/// otherwise, <see langword="false" />.</returns>
|
|
|
|
|
public static Boolean CanConvertTo<TResult>() => TypeDescriptor.GetConverter(typeof(TResult)).CanConvertFrom(typeof(String));
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempts to convert a string to the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="type">The type resulting from the conversion.</param>
|
|
|
|
|
/// <param name="str">The string to convert.</param>
|
|
|
|
|
/// <param name="result">When this method returns <see langword="true" />,
|
|
|
|
|
/// the result of the conversion. This parameter is passed uninitialized.</param>
|
|
|
|
|
/// <returns><see langword="true" /> if the conversion is successful;
|
|
|
|
|
/// otherwise, <see langword="false" />.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempts to convert a string to the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="TResult">The type resulting from the conversion.</typeparam>
|
|
|
|
|
/// <param name="str">The string to convert.</param>
|
|
|
|
|
/// <param name="result">When this method returns <see langword="true" />,
|
|
|
|
|
/// the result of the conversion. This parameter is passed uninitialized.</param>
|
|
|
|
|
/// <returns><see langword="true" /> if the conversion is successful;
|
|
|
|
|
/// otherwise, <see langword="false" />.</returns>
|
|
|
|
|
public static Boolean TryConvertTo<TResult>(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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts a string to the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="type">The type resulting from the conversion.</param>
|
|
|
|
|
/// <param name="str">The string to convert.</param>
|
|
|
|
|
/// <returns>An instance of <paramref name="type" />.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
|
|
|
|
/// <exception cref="StringConversionException">The conversion was not successful.</exception>
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts a string to the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="TResult">The type resulting from the conversion.</typeparam>
|
|
|
|
|
/// <param name="str">The string to convert.</param>
|
|
|
|
|
/// <returns>An instance of <typeparamref name="TResult" />.</returns>
|
|
|
|
|
/// <exception cref="StringConversionException">
|
|
|
|
|
/// The conversion was not successful.
|
|
|
|
|
/// </exception>
|
|
|
|
|
public static TResult ConvertTo<TResult>(String str) {
|
|
|
|
|
try {
|
|
|
|
|
return (TResult)TypeDescriptor.GetConverter(typeof(TResult)).ConvertFromInvariantString(str);
|
|
|
|
|
} catch(Exception e) when(!e.IsCriticalException()) {
|
|
|
|
|
throw new StringConversionException(typeof(TResult), e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempts to convert an array of strings to an array of the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="type">The type resulting from the conversion of each
|
|
|
|
|
/// element of <paramref name="strings"/>.</param>
|
|
|
|
|
/// <param name="strings">The array to convert.</param>
|
|
|
|
|
/// <param name="result">When this method returns <see langword="true" />,
|
|
|
|
|
/// the result of the conversion. This parameter is passed uninitialized.</param>
|
|
|
|
|
/// <returns><see langword="true" /> if the conversion is successful;
|
|
|
|
|
/// otherwise, <see langword="false" />.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
|
|
|
|
public static Boolean TryConvertTo(Type type, String[] strings, out Object? result) {
|
|
|
|
|
if(strings == null) {
|
|
|
|
|
result = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Func<String[], (Boolean Success, Object Result)> method = GenericTryConvertToMethods.GetOrAdd(type, BuildNonGenericTryConvertLambda);
|
|
|
|
|
(Boolean success, Object methodResult) = method(strings);
|
|
|
|
|
result = methodResult;
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempts to convert an array of strings to an array of the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="TResult">The type resulting from the conversion of each
|
|
|
|
|
/// element of <paramref name="strings"/>.</typeparam>
|
|
|
|
|
/// <param name="strings">The array to convert.</param>
|
|
|
|
|
/// <param name="result">When this method returns <see langword="true" />,
|
|
|
|
|
/// the result of the conversion. This parameter is passed uninitialized.</param>
|
|
|
|
|
/// <returns><see langword="true" /> if the conversion is successful;
|
|
|
|
|
/// otherwise, <see langword="false" />.</returns>
|
|
|
|
|
public static Boolean TryConvertTo<TResult>(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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts an array of strings to an array of the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="type">The type resulting from the conversion of each
|
|
|
|
|
/// element of <paramref name="strings"/>.</param>
|
|
|
|
|
/// <param name="strings">The array to convert.</param>
|
|
|
|
|
/// <returns>An array of <paramref name="type" />.</returns>
|
|
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="type" /> is <see langword="null" />.</exception>
|
|
|
|
|
/// <exception cref="StringConversionException">The conversion of at least one
|
|
|
|
|
/// of the elements of <paramref name="strings"/>was not successful.</exception>
|
|
|
|
|
public static Object? ConvertTo(Type type, String[] strings) {
|
|
|
|
|
if(strings == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Func<String[], Object> method = GenericConvertToMethods.GetOrAdd(type, BuildNonGenericConvertLambda);
|
|
|
|
|
return method(strings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts an array of strings to an array of the specified type.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="TResult">The type resulting from the conversion of each
|
|
|
|
|
/// element of <paramref name="strings"/>.</typeparam>
|
|
|
|
|
/// <param name="strings">The array to convert.</param>
|
|
|
|
|
/// <returns>An array of <typeparamref name="TResult" />.</returns>
|
|
|
|
|
/// <exception cref="StringConversionException">The conversion of at least one
|
|
|
|
|
/// of the elements of <paramref name="strings"/>was not successful.</exception>
|
|
|
|
|
public static TResult[]? ConvertTo<TResult>(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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts a expression, if the type can be converted to string, to a new expression including
|
|
|
|
|
/// the conversion to string.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="type">The type.</param>
|
|
|
|
|
/// <param name="str">The string.</param>
|
|
|
|
|
/// <returns>A new expression where the previous expression is converted to string.</returns>
|
|
|
|
|
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<String[], (Boolean Success, Object Result)> BuildNonGenericTryConvertLambda(Type type) {
|
|
|
|
|
MethodInfo? methodInfo = TryConvertToInternalMethod?.MakeGenericMethod(type);
|
|
|
|
|
ParameterExpression parameter = Expression.Parameter(typeof(String[]));
|
|
|
|
|
MethodCallExpression body = Expression.Call(methodInfo, parameter);
|
|
|
|
|
Expression<Func<String[], (Boolean Success, Object Result)>> lambda = Expression.Lambda<Func<String[], (Boolean Success, Object Result)>>(body, parameter);
|
|
|
|
|
return lambda.Compile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static (Boolean Success, Object? Result) TryConvertToInternal<TResult>(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<String[], Object> BuildNonGenericConvertLambda(Type type) {
|
|
|
|
|
MethodInfo? methodInfo = ConvertToInternalMethod?.MakeGenericMethod(type);
|
|
|
|
|
ParameterExpression parameter = Expression.Parameter(typeof(String[]));
|
|
|
|
|
MethodCallExpression body = Expression.Call(methodInfo, parameter);
|
|
|
|
|
Expression<Func<String[], Object>> lambda = Expression.Lambda<Func<String[], Object>>(body, parameter);
|
|
|
|
|
return lambda.Compile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Object ConvertToInternal<TResult>(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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-04 18:57:18 +01:00
|
|
|
|
}
|