Как преобразовать в определенный тип в общей версии TryParse()?
У меня есть следующий сценарий, где я хочу передать строку и общий тип:
public class Worker {
public void DoSomeWork<T>(string value)
where T : struct, IComparable<T>, IEquatable<T> { ... }
}
В какой-то момент по пути мне нужно преобразовать значение строки в значение T
. Но я не хочу делать прямой конвертировать, поскольку мне нужно выполнить некоторую логику, если строка не может быть преобразована в тип T
.
Я думал, что могу попробовать использовать Convert.ChangeType()
, но у этой проблемы есть проблема, что если она не будет конвертироваться, это вызовет исключение, и я буду использовать метод DoSomeWork()
достаточно часто, чтобы не полагаться на try/catch, чтобы определить, действительно ли преобразован.
Итак, это заставило меня задуматься, я знаю, что буду работать с числовыми типами, следовательно, T будет любым из следующих: int
, uint
, short
, ushort
, long
, ulong
, byte
, sbyte
, decimal
, float
, double
. Зная это, я подумал, что можно было бы найти более быстрое решение, работающее с тем, что я знаю, что буду использовать числовые типы (обратите внимание, если T
не является числовым типом, я выдаю исключение)...
public class NumericWorker {
public void DoSomeWork<T>(string value)
where T : struct, IComparable<T>, IEquatable<T>
{
ParseDelegate<T> tryConverter =
SafeConvert.RetreiveNumericTryParseDelegate<T>();
...
}
}
public class SafeConvert
{
public delegate bool ParseDelegate<T>(string value, out T result);
public static ParseDelegate<T> RetreiveNumericTryParseDelegate<T>()
where T : struct, IComparable<T>, IEquatable<T>
{
ParseDelegate<T> tryParseDelegate = null;
if (typeof(T) == typeof(int))
{
tryParseDelegate = (string v, out T t) =>
{
int typedValue;
bool result = int.TryParse(v, out typedValue);
t = result ? (T)typedValue : default(T);
//(T)Convert.ChangeType(typedValue, typeof(T)) : default(T);
return result;
};
}
else if (typeof(T) == typeof(uint)) { ... }
else if (typeof(T) == typeof(short)) { ... }
else if (typeof(T) == typeof(ushort)) { ... }
else if (typeof(T) == typeof(long)) { ... }
else if (typeof(T) == typeof(ulong)) { ... }
else if (typeof(T) == typeof(byte)) { ... }
else if (typeof(T) == typeof(sbyte)) { ... }
else if (typeof(T) == typeof(decimal)) { ... }
else if (typeof(T) == typeof(float)) { ... }
else if (typeof(T) == typeof(double)) { ... }
return tryParseDelegate;
}
}
Но вышеизложенная проблема состоит в том, что я не могу написать t = result ? (T)typedValue : default(T);
, поскольку приведение typedValue
в T
вызывает проблемы и единственный способ, которым я смог обойти это до сих пор, - написать (T)Convert.ChangeType(typedValue, typeof(T))
. Но если я это сделаю, я просто делаю другой конверт.
Следовательно, мне было интересно, знает ли кто-нибудь, как я мог бы исправить эту проблему (если вы считаете, что проблема с ChangeType()
является проблемой) или если есть лучшее решение, которое я не рассматривал.
Ответы
Ответ 1
t = результат? (T) typedValue: по умолчанию (T);
Попробуйте:
t = result ? (T)(object)typedValue : default(T);
Да, дженерики могут временами раздражать.
FWIW, я использую гораздо более простой обертку вокруг Convert.ChangeType()
, который просто выполняет предварительную проверку для пустых строк. Если вы не используете это для неконтролируемого ввода пользователем, этого, вероятно, будет достаточно.
Ответ 2
Учитывая это:
следовательно, T будет любым из следующего: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double.
Я бы порекомендовал просто использовать Convert.ChangeType и не беспокоиться об этом. Единственный раз, когда вы получите исключение, - это когда ваша строка неверно определена, и в этом случае вы можете вернуть значение по умолчанию (T).
т
try
{
result = Convert.ChangeType(value, typeof(T));
}
catch
{
result = default(T);
}
Ответ 3
ToType является общим параметром здесь. Это работает для типов с нулевым значением, на случай, если вам это нужно. Вы можете извлечь свой основной метод как универсальный конвертер, который будет конвертировать в любой тип, включая nullables.
ToType result = default(ToType);
result = ChangeType<ToType>(typedValue);
private T ChangeType<T>(object o)
{
Type conversionType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
return (T)Convert.ChangeType(o, conversionType);
}
Ответ 4
Вы можете попробовать что-то простое
public static T ConvertValue<T,U>(U value) where U : IConvertible {
return (T)ConvertValue(value, typeof(T));
}
public static object ConvertValue(IConvertible value, Type targetType) {
return Convert.ChangeType(value, targetType);
}
Ответ 5
Почему бы просто не использовать отражение и использовать встроенные методы TryParse? Довольно много для каждого родного типа, за исключением Guid.
public static Parser<T> GetParser<T>(T defaultResult)
where T : struct
{
// create parsing method
Parser<T> parser = (string value, out T result) =>
{
// look for TryParse(string value,out T result)
var parseMethod =
typeof(T).GetMethods()
.Where(p => p.Name == "TryParse")
.Where(p => p.GetParameters().Length == 2)
.Single();
// make parameters, leaving second element uninitialized means out/ref parameter
object[] parameters = new object[2];
parameters[0] = value;
// run parse method
bool success = (bool)parseMethod.Invoke(null, parameters);
// if successful, set result to output
if (!success)
{
result = (T)parameters[1];
}
else
{
result = defaultResult;
}
return success;
};
return parser;
}