如何在C#中创建通用数字解析器?
要将字符串解析为int,可以调用Int32.Parse(string)
,double, Double.Parse(string)
,long, Int64.Parse(string)
等等。
是否可以创建一个使其成为通用的方法,例如, ParseString(string)
? 其中T
可以是Int32
, Double
等。我注意到类型的数量没有实现任何公共接口,并且Parse
方法没有任何公共父级。
有没有办法实现这个或类似的东西?
您基本上必须使用reflection来查找相关的静态Parse
方法,调用它,并将返回值强制转换回T
或者,您可以使用Convert.ChangeType
或获取相关的TypeDescriptor
和关联的TypeConverter
。
更有限但有效(并且在某些方面更简单)的方法是保持字典从类型到解析委托 – 将委托转换为Func
并调用它。 这将允许您对不同类型使用不同的方法,但您需要知道转换为预先所需的类型。
无论你做什么,你都无法指定一个通用的约束,它会在编译时使其安全。 真的,你需要像我这样的静态接口的想法。 编辑:如上所述,有IConvertible
接口,但这并不一定意味着你将能够从string
转换。 另一种类型可以实现IConvertible
而无需从字符串转换为该类型。
实际上,标准数字类型确实实现了一个通用接口: IConvertible 。 这是Convert.ChangeType
使用的那个。
不幸的是,没有TryParse
等价物,如果无法解析字符串,它将抛出exception。
作为旁注,BCL团队似乎完全忘记了整个“转换”区域。 自.NET Framework 1以来没有什么新东西(除了TryParse方法)。
这非常hackish,但它使用Newtonsoft.Json(Json.NET) :
JsonConvert.DeserializeObject("24.11"); // Type == System.Double - Value: 24.11 JsonConvert.DeserializeObject("29.4"); // Type == System.Int32 - Value: 29
我编写了一些代码,使用reflection来查找类型上的Parse
/ TryParse
方法,并从generics函数中访问它们:
private static class ParseDelegateStore { public static ParseDelegate Parse; public static TryParseDelegate TryParse; } private delegate T ParseDelegate (string s); private delegate bool TryParseDelegate (string s, out T result); public static T Parse (string s) { ParseDelegate parse = ParseDelegateStore .Parse; if (parse == null) { parse = (ParseDelegate )Delegate.CreateDelegate(typeof(ParseDelegate ), typeof(T), "Parse", true); ParseDelegateStore .Parse = parse; } return parse(s); } public static bool TryParse (string s, out T result) { TryParseDelegate tryParse = ParseDelegateStore .TryParse; if (tryParse == null) { tryParse = (TryParseDelegate )Delegate.CreateDelegate(typeof(TryParseDelegate ), typeof(T), "TryParse", true); ParseDelegateStore .TryParse = tryParse; } return tryParse(s, out result); }
https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Util/Conversion.cs
但我没有对它们进行过多的测试,所以它们可能会因为每种类型都有一些错误/不能正常工作。 error handling也有点缺乏。
并且他们没有文化不变解析的重载。 所以你可能需要添加它。
是的,可以从字符串中解析的类型很可能具有静态Parse
和TryParse
重载,您可以通过Jon建议的reflection找到这些重载。
private static Func GetParser() { // 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 }); }
为了性能,您还可以构建调用它们的lambda表达式,因为Invoke
方法接受方法参数作为object[]
,这是一个不必要的分配,如果您的参数包含值类型,则导致装箱。 它还将结果作为object
返回,当您的类型为值类型时,该object
也会导致装箱。
private static Func GetParser() { // 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>(call, param); return lambda.Compile(); }
调用已编译的lambda表达式与调用原始解析方法本身一样快,但首先构建和编译它不是。 这就是为什么,像Jon建议的那样,我们应该缓存生成的委托。
我使用静态generics类来缓存ValueString中的解析器。
private static class Parser { public static readonly Func Parse = InitParser(); private static Func InitParser() { // Our initialization logic above. } }
之后,您的解析方法可以这样写:
上述就是C#学习教程:如何在C#中创建通用数字解析器?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
public static T Parse(string s) { return Parser .Parse(s); }
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/961611.html