How to make a general number analyzer in C #?

To parse a string into int, Int32.Parse(string) is called, for double, Double.Parse(string) , long, Int64.Parse(string) , etc.

Is it possible to create a method that makes it universal, for example, ParseString<T>(string) ? where T can be Int32 , Double , etc. I noticed that some types do not have a common interface, and Parse methods do not have a common parent.

Is there a way to achieve this or something similar to this?

+11
string c # types numbers parsing
May 28 '11 at 7:35 a.m.
source share
5 answers

Basically, you would have to use reflection to find the appropriate static Parse method, call it, and cast the return value back to T Alternatively, you can use Convert.ChangeType or get the corresponding TypeDescriptor and its associated TypeConverter .

A more limited but effective (and in a sense simple) approach is to save the dictionary from type to the parser delegate - cast the delegate to Func<string, T> and call it. This would allow you to use different methods for different types, but you would need to know the types that need to be converted to a preliminary one.

No matter what you do, you cannot specify a general restriction that would make it safe at compile time. In fact, you need something like my idea of static interfaces for this kind of thing. UPDATE: As already mentioned, there is an IConvertible interface, but that does not necessarily mean that you can convert from string . Another type may implement IConvertible without any way of converting to this type from the string.

+13
May 28 '11 at 7:37 a.m.
source share

In fact, standard room types do implement a common interface: IConvertible . This is the one that Convert.ChangeType uses.

Unfortunately, there is no equivalent to TryParse , it will throw exceptions if the string cannot be parsed.

By the way, it seems that this entire area of ​​"conversion" has been completely forgotten by the BCL team. There is nothing new since the .NET Framework 1 (except for TryParse methods).

+5
May 28 '11 at 7:51
source share

This is very funny, but works using 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 
+2
May 28 '11 at 8:35 a.m.
source share

I wrote code that uses reflection to find Parse / TryParse for a type and access them from common functions:

 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

But I did not test them too much, so they might have some errors / not work correctly with each type. Error handling is also a bit missing.

And they do not have overloads to analyze the invariance of culture. Therefore, you probably need to add this.

0
May 28 '11 at 8:42
source share

Yes, types that can be parsed from a string are more likely to have Parse and TryParse static overloads, which you can find through reflection, as John suggested.

 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 create lambda expressions that call them, because the Invoke method accepts the parameters of the method as object[] , which is an unnecessary distribution, and if your parameters include value types, it calls boxing. It also returns the result as an object , which also calls boxing when your type is a value type.

 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 a compiled lambda expression is essentially as fast as calling the most original parsing method, but creating and compiling it in the first place is not. That is why, again, as John suggested, we have to cache the delegate we received.

I am using a static generic class to cache parsers in a 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 as follows:

 public static T Parse<T>(string s) { return Parser<T>.Parse(s); } 
0
Nov 18 '17 at 11:25
source share



All Articles