没有拳击的通用解析方法

我正在尝试编写一个通用的Parse方法,该方法转换并返回NamedValueCollection中的强类型值。 我尝试了两种方法,但这两种方法都通过装箱和拆箱来获得价值。 有谁知道避免拳击的方法? 如果你在生产中看到这个你会不喜欢它,它的性能有多糟糕?

Usuage:

var id = Request.QueryString.Parse("id"); 

尝试#1:

 public static T Parse(this NameValueCollection col, string key) { string value = col[key]; if (string.IsNullOrEmpty(value)) return default(T); if (typeof(T) == typeof(int)) { //return int.Parse(value); // cannot convert int to T //return (T)int.Parse(value); // cannot convert int to T return (T)(object)int.Parse(value); // works but boxes } if (typeof(T) == typeof(long)) { return (T)(object)long.Parse(value); // works but boxes } ... return default(T); } 

尝试#2(使用reflection):

 public static T Parse(this NameValueCollection col, string key) { string value = col[key]; if (string.IsNullOrEmpty(value)) return default(T); try { var parseMethod = typeof(T).GetMethod("Parse", new Type[] { typeof(string) }); if (parseMethod == null) return default(T); // still boxing because invoke returns an object var parsedVal = parseMethod.Invoke(null, new object[] { value }); return (T)parsedVal; } // No Proper Parse Method found catch(AmbiguousMatchException) { } return default(T); } 

我认为你过度估计拳击/拆箱的影响。 解析方法将有更大的开销(字符串解析),使拳击开销相形见绌。 此外,所有if语句都会产生更大的影响。 反思对所有人产生了最大的影响。

我不希望在生产中看到这种代码,因为有一种更清洁的方法。 我遇到的主要问题是你需要覆盖所有案件的大量if语句以及有人可以将任何旧类型传递给它的事实。

我要做的是为我想要解析的每个类型编写一个解析函数(即ParseInt())。 它更清晰,并且很好地定义了该函数将尝试做什么。 此外,使用短静态方法,编译器更可能内联它们,从而保存函数调用。

我认为这是一个不好的generics应用程序,这样做的任何特殊原因?

 public static T Parse(this NameValueCollection col, string key) { return (T)Convert.ChangeType(col[key], typeof(T)); } 

我不完全确定ChangeType框是不是(我想阅读文档会告诉我,但我现在已经按时间了),但至少它摆脱了所有类型检查。 虽然拳击开销不是很高,所以我不会太担心它。 如果您担心运行时类型的一致性,我会将函数编写为:

 public static T Parse(this NameValueCollection col, string key) { T value; try { value = (T)Convert.ChangeType(col[key], typeof(T)); } catch { value = default(T); } return value; } 

这样,如果由于某种原因无法转换值,函数将不会爆炸。 这意味着,当然,您必须检查返回的值(无论如何您都必须这样做,因为用户可以编辑查询字符串)。

我将添加一些未记录的方式:

 public static T Convert() { if (typeof(T) == typeof(int)) { int a = 5; T value = __refvalue(__makeref(a), T); return value; } else if (typeof(T) == typeof(long)) { long a = 6; T value = __refvalue(__makeref(a), T); return value; } throw new NotImplementedException(); } 

关于它们的文档很少,但它们的工作原理是C#4.0。 例如,在这里阅读C#的隐藏function? 请记住,无证件意味着不受支持,等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等

为了更好的可读性,您可以使用具有匿名函数的通用字典,如下所示:

 var parserFuncs = new Dictionary>() { { typeof(int), p => (int) int.Parse(p) }, { typeof(bool), p => (bool) bool.Parse(p) }, { typeof(long), p => (long) long.Parse(p) }, { typeof(short), p => (short) short.Parse(p) }, { typeof(DateTime), p => (DateTime) DateTime.Parse(p) } /* ...same for all the other primitive types */ }; return (T) parserFuncs[typeof(T)](value); 

另一个实现建议,使用TryParse或Parse方法和通用方法。 我最初写这篇文章是为了将从csv文件解析的字符串转换为不同的类型,int,decimal,list等。

  public static bool TryParse(this string value, out T newValue, T defaultValue = default(T)) where T : struct, IConvertible { newValue = defaultValue; try { newValue = (T)Convert.ChangeType(value, typeof(T)); } catch { return false; } return true; } public static T Parse(this string value) where T : struct, IConvertible { return (T) Convert.ChangeType(value, typeof (T)); } 

这里,try parse方法首先将newValue设置为默认值,然后尝试将value转换为类型T并将newValue作为类型T返回。如果转换失败,则返回默认值T.

Parse方法只是尝试进行转换,但是如果它不是故障安全的,并且如果转换失败将抛出exception。

 int value = int.Parse(Request.QueryString["RecordID"]); 

根据Robert Wagner的逻辑,这是一个实施建议,但使用通用方法来减少重复:

 public static int ParseInt32(this NameValueCollection col, string key) { return Parse(col, key, int.Parse); } public static double ParseDouble(this NameValueCollection col, string key) { return Parse(col, key, double.Parse); } private static T Parse(NameValueCollection col, string key, Func parse) { string value = col[key]; if (string.IsNullOrEmpty(value)) return default(T); return parse(value); } 

说实话,为零或空字符串返回零会吓到我; 如果某些值合法为零,这可能会导致问题。 相反,我会让方法返回nullables( int?double?等),这比用于框架TryParse方法的out-parameter模式稍微紧凑一些。 你可以这样做:

 public static int? ParseInt32(this NameValueCollection col, string key) { return Parse(col, key, int.Parse); } public static double? ParseDouble(this NameValueCollection col, string key) { return Parse(col, key, double.Parse); } private static T? Parse(NameValueCollection col, string key, Func parse) where T : struct { string value = col[key]; if (string.IsNullOrEmpty(value)) return default(T?); return parse(value); } 

但是,对于非数字的非空或空字符串,这仍然会引发exception。 最好使用TryParse。 内置的Func委托不支持ref或out参数,因此你必须声明自己的委托类型,但这是相当简单的。