如何在 TryParse() 的泛型版本中转换为特定类型?
Posted
技术标签:
【中文标题】如何在 TryParse() 的泛型版本中转换为特定类型?【英文标题】:How can I convert to a specific type in a generic version of TryParse()? 【发布时间】:2009-07-09 23:33:31 【问题描述】:我有以下场景,我想传入字符串和泛型类型:
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
、@ 987654331@、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
的转换会导致问题,到目前为止我能够解决它的唯一方法是写@987654342 @。但如果我这样做,我只是在做另一个转换。
因此我想知道是否有人知道我可以如何解决这个问题(如果您认为执行 ChangeType()
是一个问题),或者是否有更好的解决方案我没有考虑过。
【问题讨论】:
【参考方案1】:t = 结果? (T)typedValue : 默认值(T);
试试:
t = result ? (T)(object)typedValue : default(T);
是的,泛型有时会有点烦人。
FWIW,我在Convert.ChangeType()
周围使用a much simpler wrapper,它只是对空字符串进行预检查。除非您将其用于未经检查的用户输入,否则可能就足够了。
【讨论】:
我使用它的目的是验证用户输入... :( 这就是为什么我不想使用 changetype 和 try/catch 因为谁知道用户会输入什么... 嘿,好的。 :-) FWIW,我不会太担心捕获ChangeType()
抛出的异常的效率方面,但是如果您担心样式,那么您的解决方案看起来不错。请注意,您可以通过将 API 简化为单个泛型方法来简化 API,其中 T 由 out
参数隐式指定。
为了简单起见,你说的“其中 T 由 out 参数隐式指定”是什么意思??
你的意思是在委托内部进行类型测试...如果是这样,我这样做的原因是为了性能,这意味着一旦我进行测试并返回一个委托,我就可以运行尽可能多地委托,而性能副作用最小......
啊,明白了 - 想知道代表的意义是什么。希望能帮助到你! :-)【参考方案2】:
鉴于此:
因此 T 将是以下任何一种:int、uint、short、ushort、long、ulong、byte、sbyte、decimal、float、double。
我建议只使用 Convert.ChangeType,而不用担心它。唯一出现异常的情况是您的字符串格式错误,在这种情况下,您可以返回 default(T)。
即:
try
result = Convert.ChangeType(value, typeof(T));
catch
result = default(T);
【讨论】:
正如我给@Shog9 的评论中提到的那样,问题是因为我使用它来验证用户输入我担心生成异常的开销。 afaik 生成一个异常,您知道每次运行该方法时可能会发生一次异常,这是一项昂贵的操作,因此我尝试改用委托。考虑到您建议的我尝试使用的委托方法的版本仍然值得使用,您会说吗? 就个人而言,我仍然会使用它。在这种情况下,我不会担心异常的开销,特别是因为如果它用于用户输入,您不需要在紧密循环中调用它(因为用户可能无法像您可以处理异常)。即使使用其他方法,您的 switch 语句、委托调用等也会有开销,与我上面的 6 行相比,维护起来很糟糕。 我同意里德的观点。异常在最坏的情况下(冷)可能需要几十毫秒的时间;但如果您正在验证 one 用户输入字符串,那几乎不会引起注意。 结果 = (T)Convert.ChangeType(value, typeof(T));您还必须从 ChangeType 进行转换。【参考方案3】:ToType 是这里的通用参数。这适用于可为空的类型,以防万一您需要它。您可以将您的主要方法提取为通用转换器,它将转换为任何类型,包括可为空的。
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);
【讨论】:
如果我使用这种方法,这如何解决对 try/catch 块的需求?? 不会抛出异常,因为您使用的是 TryParse。如果 TryParse 的结果为真,那么除非您传入完全不同的通用参数,否则转换将起作用。这是你的困境吗? 抱歉,您不能传递不同的泛型参数,因为您有一个关于 typeof(T) 的 if 语句......那么您的问题到底是什么?【参考方案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;
【讨论】:
如果您对其进行测试,您可以在 11 秒内完成超过 100,000 次解析。我使用耗时 14 秒的原生表达式树与 .net 4 进行了比较(尽管在 .net 4 发布时可能不会反映性能)以上是关于如何在 TryParse() 的泛型版本中转换为特定类型?的主要内容,如果未能解决你的问题,请参考以下文章