我有以下场景,我想传入字符串和泛型类型:
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
,{{1 },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))
是一个问题),或者是否有一个我没有考虑的更好的解决方案。
答案 0 :(得分:39)
t =结果? (T)typedValue:默认(T);
尝试:
t = result ? (T)(object)typedValue : default(T);
是的,仿制药有时会有点烦人。
FWIW,我在Convert.ChangeType()
周围使用a much simpler wrapper,只是对空字符串进行预检查。除非您将此用于未经检查的用户输入,否则这可能就足够了。
答案 1 :(得分:12)
鉴于此:
因此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);
}
答案 2 :(得分:5)
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);
}
答案 3 :(得分:2)
你可以尝试一些简单的事情
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);
}
答案 4 :(得分:1)
为什么不使用反射并使用内置的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;
}