C# 如何在通用版本的 TryParse() 中转换为特定类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1106974/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I convert to a specific type in a generic version of TryParse()?
提问by vdh_ant
I have the following scenario where I want to pass in string and a generic type:
我有以下场景,我想传入字符串和泛型类型:
public class Worker {
public void DoSomeWork<T>(string value)
where T : struct, IComparable<T>, IEquatable<T> { ... }
}
At some point along the way I need to convert the string value to its T
value. But I don't want to do a straight convert as I need to perform some logic if the string cannot be converted to type T
.
在此过程中的某个时刻,我需要将字符串值转换为其T
值。但我不想直接转换,因为如果字符串无法转换为 type ,我需要执行一些逻辑T
。
I was thinking that I could try using Convert.ChangeType()
but this has the problem that if it doesn't convert it will throw an exception and I will be running the DoSomeWork()
method often enough to not have to rely on a try/catch to determine whether the convert is valid.
我在想我可以尝试使用,Convert.ChangeType()
但这有一个问题,如果它不转换,它会抛出一个异常,我将DoSomeWork()
经常运行该方法,而不必依赖 try/catch 来确定转换是否是有效的。
So this got me thinking, I know that I will be working with numeric types, hence T will be any of the following: int
, uint
, short
, ushort
, long
, ulong
, byte
, sbyte
, decimal
, float
, double
. Knowing this I thought that it might be possible to come up with a faster solution working with the fact that I know I will be using numeric types (note if T
isn't a numeric type I throw an exception)...
所以这让我想到,我知道我将使用数字类型,因此 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;
}
}
But the above has the problem that I can't write t = result ? (T)typedValue : default(T);
as the casting of typedValue
to T
causes issues and the only way I have been able to get around it thus far is by writing (T)Convert.ChangeType(typedValue, typeof(T))
. But if I do this I am just doing another convert.
但是上面有我无法写的问题,t = result ? (T)typedValue : default(T);
因为typedValue
to的铸造T
会导致问题,到目前为止我能够绕过它的唯一方法是编写(T)Convert.ChangeType(typedValue, typeof(T))
. 但如果我这样做,我只是在做另一个转换。
Hence I was wondering if anyone knows how I could fix this problem (if you think doing the ChangeType()
is a problem) or if there is a better solution altogether that I haven't considered.
因此我想知道是否有人知道我如何解决这个问题(如果你认为这样做ChangeType()
是一个问题)或者是否有一个我没有考虑过的更好的解决方案。
采纳答案by Shog9
t = result ? (T)typedValue : default(T);
t = 结果?(T)typedValue : 默认(T);
Try:
尝试:
t = result ? (T)(object)typedValue : default(T);
Yes, generics can be kinda annoying at times.
是的,泛型有时会有点烦人。
FWIW, I use a much simpler wrapperaround Convert.ChangeType()
that just does a pre-check for empty strings. Unless you're using this for un-checked user input, that'll probably be enough.
FWIW,我使用了一个更简单的包装器,Convert.ChangeType()
它只是对空字符串进行预检查。除非您将其用于未经检查的用户输入,否则这可能就足够了。
回答by Reed Copsey
Given this:
鉴于这种:
hence T will be any of the following: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double.
因此 T 将是以下任何一个:int、uint、short、ushort、long、ulong、byte、sbyte、decimal、float、double。
I would recommend just using Convert.ChangeType, and not worrying about it. The only time you'll get an exception is when your string is misformatted, in which case, you can return default(T).
我建议只使用 Convert.ChangeType,而不必担心。您唯一会遇到异常的情况是您的字符串格式错误,在这种情况下,您可以返回 default(T)。
ie:
IE:
try
{
result = Convert.ChangeType(value, typeof(T));
}
catch
{
result = default(T);
}
回答by Stan R.
ToType being the generic parameter here. This works for nullable types, just in case you needed it. You can extract your main method to be a generic converter, that will convert to any type, including nullables.
ToType 是这里的通用参数。这适用于可为空类型,以防万一您需要它。您可以将 main 方法提取为通用转换器,该转换器将转换为任何类型,包括可空类型。
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);
}
回答by Darren Kopp
Why not just use reflection and use the built in TryParse methods? Pretty much one for every native type with the exception of Guid.
为什么不直接使用反射并使用内置的 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;
}
回答by Herman Schoenfeld
You can try something simple
你可以尝试一些简单的
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);
}