To parse a string to an int, one calls Int32.Parse(string)
, for double, Double.Parse(string)
, for long, Int64.Parse(string)
, and so on..
要将字符串解析为int,可以调用Int32.Parse(string),double,Double.Parse(string),long,Int64.Parse(string)等等。
Is it possible to create a method that makes it generic, for example, ParseString<T>(string)
? where T can be Int32, Double, etc. I notice the number types don't implement any common interface, and the Parse methods don't have any common parent.
是否可以创建一个使其成为通用的方法,例如,ParseString
Is there a way to achieve this or something similar to this?
有没有办法实现这个或类似的东西?
5 个解决方案
#1
11
You'd basically have to use reflection to find the relevant static Parse
method, invoke it, and cast the return value back to T
. Alternatively, you could use Convert.ChangeType
or get the relevant TypeDescriptor
and associated TypeConverter
.
您基本上必须使用反射来查找相关的静态Parse方法,调用它,并将返回值强制转换回T.或者,您可以使用Convert.ChangeType或获取相关的TypeDescriptor和关联的TypeConverter。
A more limited but efficient (and simple, in some ways) approach would be to keep a dictionary from type to parsing delegate - cast the delegate to a Func<string, T>
and invoke it. That would allow you to use different methods for different types, but you'd need to know the types you needed to convert to up-front.
更有限但有效(并且在某些方面更简单)的方法是保持字典从类型到解析委托 - 将委托转换为Func
Whatever you do, you won't be able to specify a generic constraint which would make it safe at compile-time though. Really you need something like my idea of static interfaces for that kind of thing. EDIT: As mentioned, there's the IConvertible
interface, but that doesn't necessarily mean that you'll be able to convert from string
. Another type could implement IConvertible
without having any way of converting to that type from a string.
无论你做什么,你都无法指定一个通用的约束,它会在编译时使其安全。真的,你需要像我这样的静态接口的想法。编辑:如上所述,有IConvertible接口,但这并不一定意味着你将能够从字符串转换。另一种类型可以实现IConvertible,而无需从字符串转换为该类型。
#2
4
Actually, the standard number types do implement a common interface: IConvertible. This is the one that Convert.ChangeType
use.
实际上,标准数字类型确实实现了一个通用接口:IConvertible。这是Convert.ChangeType使用的那个。
Unfortunately, there is no TryParse
equivalent, it will throw exceptions if the string cannot be parsed.
不幸的是,没有TryParse等价物,如果无法解析字符串,它将抛出异常。
As a side note, it seems this whole "conversion" area has been completely forgotten by the BCL team. There is nothing new there since .NET Framework 1 (except from TryParse methods).
作为旁注,BCL团队似乎完全忘记了整个“转换”区域。自.NET Framework 1以来没有什么新东西(除了TryParse方法)。
#3
1
This is very hackish, but it works using Newtonsoft.Json (Json.NET):
这非常hackish,但它使用Newtonsoft.Json(Json.NET):
JsonConvert.DeserializeObject<double>("24.11");
// Type == System.Double - Value: 24.11
JsonConvert.DeserializeObject<int>("29.4");
// Type == System.Int32 - Value: 29
#4
0
I have written some code that uses reflection to find Parse
/TryParse
methods on a type and access these from generic functions:
我编写了一些代码,使用反射来查找类型上的Parse / TryParse方法,并从泛型函数中访问它们:
private static class ParseDelegateStore<T>
{
public static ParseDelegate<T> Parse;
public static TryParseDelegate<T> TryParse;
}
private delegate T ParseDelegate<T>(string s);
private delegate bool TryParseDelegate<T>(string s, out T result);
public static T Parse<T>(string s)
{
ParseDelegate<T> parse = ParseDelegateStore<T>.Parse;
if (parse == null)
{
parse = (ParseDelegate<T>)Delegate.CreateDelegate(typeof(ParseDelegate<T>), typeof(T), "Parse", true);
ParseDelegateStore<T>.Parse = parse;
}
return parse(s);
}
public static bool TryParse<T>(string s, out T result)
{
TryParseDelegate<T> tryParse = ParseDelegateStore<T>.TryParse;
if (tryParse == null)
{
tryParse = (TryParseDelegate<T>)Delegate.CreateDelegate(typeof(TryParseDelegate<T>), typeof(T), "TryParse", true);
ParseDelegateStore<T>.TryParse = tryParse;
}
return tryParse(s, out result);
}
https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Util/Conversion.cs
https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Util/Conversion.cs
But I haven't tested them too much, so they might stiff have some bugs/not work correctly with every type. The error handling is a bit lacking too.
但我没有对它们进行过多的测试,所以它们可能会因为每种类型都有一些错误/不能正常工作。错误处理也有点缺乏。
And they have no overloads for culture invariant parsing. So you probably need to add that.
并且他们没有文化不变解析的重载。所以你可能需要添加它。
#5
0
Yes, the types that can be parsed from a string will most likely have static Parse
and TryParse
overloads that you can find via reflection like Jon suggested.
是的,可以从字符串中解析的类型很可能具有静态Parse和TryParse重载,您可以通过Jon建议的反射找到这些重载。
private static Func<string, T> GetParser<T>()
{
// The method we are searching for accepts a single string.
// You can add other types, like IFormatProvider to target specific overloads.
var signature = new[] { typeof(string) };
// Get the method with the specified name and parameters.
var method = typeof(T).GetMethod("Parse", signature);
// Initialize the parser delegate.
return s => (T)method.Invoke(null, new[] { s });
}
For performance you can also build lambda expressions calling them since the Invoke
method accepts the method parameters as an object[]
which is an unnecessary allocation and if your parameters include value types, causes boxing. It also returns the result as an object
which also causes boxing when your type is a value type.
为了性能,您还可以构建调用它们的lambda表达式,因为Invoke方法接受方法参数作为object []这是一个不必要的分配,如果您的参数包含值类型,则会导致装箱。它还将结果作为对象返回,当您的类型为值类型时,该对象也会导致装箱。
private static Func<string, T> GetParser<T>()
{
// Get the method like we did before.
var signature = new[] { typeof(string) };
var method = typeof(T).GetMethod("Parse", signature);
// Build and compile a lambda expression.
var param = Expression.Parameter(typeof(string));
var call = Expression.Call(method, param);
var lambda = Expression.Lambda<Func<string, T>>(call, param);
return lambda.Compile();
}
Calling the compiled lambda expression is essentially as fast as calling the original parsing method itself, but building and compiling it in the first place is not. This is why, again like Jon suggested, we should cache the resulting delegate.
调用已编译的lambda表达式与调用原始解析方法本身一样快,但首先构建和编译它不是。这就是为什么,像Jon建议的那样,我们应该缓存生成的委托。
I use a static, generic class to cache parsers in ValueString.
我使用静态泛型类来缓存ValueString中的解析器。
private static class Parser<T>
{
public static readonly Func<string, T> Parse = InitParser();
private static Func<string, T> InitParser()
{
// Our initialization logic above.
}
}
After that your parsing method can be written like this:
之后,您的解析方法可以这样写:
public static T Parse<T>(string s)
{
return Parser<T>.Parse(s);
}
#1
11
You'd basically have to use reflection to find the relevant static Parse
method, invoke it, and cast the return value back to T
. Alternatively, you could use Convert.ChangeType
or get the relevant TypeDescriptor
and associated TypeConverter
.
您基本上必须使用反射来查找相关的静态Parse方法,调用它,并将返回值强制转换回T.或者,您可以使用Convert.ChangeType或获取相关的TypeDescriptor和关联的TypeConverter。
A more limited but efficient (and simple, in some ways) approach would be to keep a dictionary from type to parsing delegate - cast the delegate to a Func<string, T>
and invoke it. That would allow you to use different methods for different types, but you'd need to know the types you needed to convert to up-front.
更有限但有效(并且在某些方面更简单)的方法是保持字典从类型到解析委托 - 将委托转换为Func
Whatever you do, you won't be able to specify a generic constraint which would make it safe at compile-time though. Really you need something like my idea of static interfaces for that kind of thing. EDIT: As mentioned, there's the IConvertible
interface, but that doesn't necessarily mean that you'll be able to convert from string
. Another type could implement IConvertible
without having any way of converting to that type from a string.
无论你做什么,你都无法指定一个通用的约束,它会在编译时使其安全。真的,你需要像我这样的静态接口的想法。编辑:如上所述,有IConvertible接口,但这并不一定意味着你将能够从字符串转换。另一种类型可以实现IConvertible,而无需从字符串转换为该类型。
#2
4
Actually, the standard number types do implement a common interface: IConvertible. This is the one that Convert.ChangeType
use.
实际上,标准数字类型确实实现了一个通用接口:IConvertible。这是Convert.ChangeType使用的那个。
Unfortunately, there is no TryParse
equivalent, it will throw exceptions if the string cannot be parsed.
不幸的是,没有TryParse等价物,如果无法解析字符串,它将抛出异常。
As a side note, it seems this whole "conversion" area has been completely forgotten by the BCL team. There is nothing new there since .NET Framework 1 (except from TryParse methods).
作为旁注,BCL团队似乎完全忘记了整个“转换”区域。自.NET Framework 1以来没有什么新东西(除了TryParse方法)。
#3
1
This is very hackish, but it works using Newtonsoft.Json (Json.NET):
这非常hackish,但它使用Newtonsoft.Json(Json.NET):
JsonConvert.DeserializeObject<double>("24.11");
// Type == System.Double - Value: 24.11
JsonConvert.DeserializeObject<int>("29.4");
// Type == System.Int32 - Value: 29
#4
0
I have written some code that uses reflection to find Parse
/TryParse
methods on a type and access these from generic functions:
我编写了一些代码,使用反射来查找类型上的Parse / TryParse方法,并从泛型函数中访问它们:
private static class ParseDelegateStore<T>
{
public static ParseDelegate<T> Parse;
public static TryParseDelegate<T> TryParse;
}
private delegate T ParseDelegate<T>(string s);
private delegate bool TryParseDelegate<T>(string s, out T result);
public static T Parse<T>(string s)
{
ParseDelegate<T> parse = ParseDelegateStore<T>.Parse;
if (parse == null)
{
parse = (ParseDelegate<T>)Delegate.CreateDelegate(typeof(ParseDelegate<T>), typeof(T), "Parse", true);
ParseDelegateStore<T>.Parse = parse;
}
return parse(s);
}
public static bool TryParse<T>(string s, out T result)
{
TryParseDelegate<T> tryParse = ParseDelegateStore<T>.TryParse;
if (tryParse == null)
{
tryParse = (TryParseDelegate<T>)Delegate.CreateDelegate(typeof(TryParseDelegate<T>), typeof(T), "TryParse", true);
ParseDelegateStore<T>.TryParse = tryParse;
}
return tryParse(s, out result);
}
https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Util/Conversion.cs
https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Util/Conversion.cs
But I haven't tested them too much, so they might stiff have some bugs/not work correctly with every type. The error handling is a bit lacking too.
但我没有对它们进行过多的测试,所以它们可能会因为每种类型都有一些错误/不能正常工作。错误处理也有点缺乏。
And they have no overloads for culture invariant parsing. So you probably need to add that.
并且他们没有文化不变解析的重载。所以你可能需要添加它。
#5
0
Yes, the types that can be parsed from a string will most likely have static Parse
and TryParse
overloads that you can find via reflection like Jon suggested.
是的,可以从字符串中解析的类型很可能具有静态Parse和TryParse重载,您可以通过Jon建议的反射找到这些重载。
private static Func<string, T> GetParser<T>()
{
// The method we are searching for accepts a single string.
// You can add other types, like IFormatProvider to target specific overloads.
var signature = new[] { typeof(string) };
// Get the method with the specified name and parameters.
var method = typeof(T).GetMethod("Parse", signature);
// Initialize the parser delegate.
return s => (T)method.Invoke(null, new[] { s });
}
For performance you can also build lambda expressions calling them since the Invoke
method accepts the method parameters as an object[]
which is an unnecessary allocation and if your parameters include value types, causes boxing. It also returns the result as an object
which also causes boxing when your type is a value type.
为了性能,您还可以构建调用它们的lambda表达式,因为Invoke方法接受方法参数作为object []这是一个不必要的分配,如果您的参数包含值类型,则会导致装箱。它还将结果作为对象返回,当您的类型为值类型时,该对象也会导致装箱。
private static Func<string, T> GetParser<T>()
{
// Get the method like we did before.
var signature = new[] { typeof(string) };
var method = typeof(T).GetMethod("Parse", signature);
// Build and compile a lambda expression.
var param = Expression.Parameter(typeof(string));
var call = Expression.Call(method, param);
var lambda = Expression.Lambda<Func<string, T>>(call, param);
return lambda.Compile();
}
Calling the compiled lambda expression is essentially as fast as calling the original parsing method itself, but building and compiling it in the first place is not. This is why, again like Jon suggested, we should cache the resulting delegate.
调用已编译的lambda表达式与调用原始解析方法本身一样快,但首先构建和编译它不是。这就是为什么,像Jon建议的那样,我们应该缓存生成的委托。
I use a static, generic class to cache parsers in ValueString.
我使用静态泛型类来缓存ValueString中的解析器。
private static class Parser<T>
{
public static readonly Func<string, T> Parse = InitParser();
private static Func<string, T> InitParser()
{
// Our initialization logic above.
}
}
After that your parsing method can be written like this:
之后,您的解析方法可以这样写:
public static T Parse<T>(string s)
{
return Parser<T>.Parse(s);
}