2009-12-10 137 views
8

说我有一个方法,接受一个int作为一个字符串,如果解析成功返回int,否则返回一个空值。泛型和可空类型

int? ParseValue(string intAsString) 
    { 
     int i; 
     if (int.TryParse(intAsString, out i)) 
      return i; 
     return null; 
    } 

该方法如何重写,以便它不仅可以与int ?,还可以使用long?,decimal?和DateTime? ?

回答

14

这很有趣,你应该提到它,因为我的东西瞎搞就这样的一天:

using System; 
using System.Reflection; 

static class Example 
{ 
    public static Tuple<Boolean, T?> TryParse<T>(this String candidate) 
     where T : struct 
    { 
     T? value = null; 
     Boolean success = false; 

     var parser = ParsingBinder<T>.GetParser(); 

     try 
     { 
       value = parser(candidate); 
       success = true; 
     } 
     catch (FormatException) { } 

     return new Tuple<Boolean,T?>(success, value); 
    } 
} 

static class ParsingBinder<T> 
{ 
    static Func<String, T> parser; 

    public static Func<String, T> GetParser() 
    { 
     if (parser == null) 
       parser = getParser(); 

     return parser; 
    } 

    static Func<String, T> getParser() 
    { 
     MethodInfo methodInfo 
      = typeof(T).GetMethod(
        "Parse", new [] { typeof(String) }); 

     if (methodInfo == null) 
       throw new Exception(
         "Unable to retrieve a \"Parse\" method for type."); 

     return (Func<String, T>)Delegate 
     .CreateDelegate(typeof(Func<String, T>), methodInfo); 
    } 
} 

这是一个类似的做法,但认为这是一个更好的TryParse方法返回一个Tuple<Boolean, T?>(这需要.NET 4)。元组的第一个属性是一个布尔值,指示解析尝试的成功或失败,第二个属性是类型为通用类型参数的可为空的值,如果解析失败,则为null,解析成功时为值。

它通过使用反射来检索泛型类型参数的静态Parse(String)方法,并调用该方法对于在我建立它作为一个扩展的方法来让你做这样的东西传递的字符串:

var intValue = "1234".TryParse<Int32>(); 
var doubleValue = "1234".TryParse<Double>(); 

不幸的是这不会对enums工作,因为他们不具备解析方法相同的签名,所以你不能使用这个扩展来解析enum,但它不会是很难破解这个高达为枚举做一个特例。

这种方法的好处之一是通过反射检索Parse方法的成本只会在第一次使用时产生,因为为所有后续使用创建静态委托。


一两件事 - 的唯一的事情就是笨重对这种做法是没有任何语言扩展或语法糖,这将使这个易于使用。我希望通过此代码实现的是使用BCL中存在的标准TryParse方法的笨重方法。

我个人认为,这一模式比较难看:

Int32 value; 
if (Int32.TryParse(someString, out value)) 
    // do something with value 

主要是因为它需要的时间提前一个变量声明和使用out参数。以上我的做法是不是真的那么好很多:

var result = someString.TryParse<Int32>(); 
if (result.Item1) 
    // do something with result.Item2 

什么是真的很酷将看到一个建有Tuple<Boolean, T?>的工作,使我们与这种类型工作的顺利开展一个C#语言的扩展,但我越感觉到,我写这篇文章似乎并不可行。

+1

假设这真的作品,+1:P – 2009-12-10 05:00:47

+0

考虑的'TryParse'没有投掷失败的异常,存在的主要目的,你的“好办法”已撤消其存在的目的:HTTP: //www.codinghorror.com/blog/archives/000358.html – 2009-12-10 05:25:32

+0

@ 280z28 - 够公平,但我抛出的例外情况不同。当你试图解析一个没有'TryParse(String)'方法的类型时,就会抛出这个异常,当你调用一个普通的'TryParse'方法时,这种方法从来不会发生。这个异常无疑会被开发人员在测试中发现,并且不会在运行时发生,因此它与被解析失败掩盖的异常不完全相同。 – 2009-12-10 05:34:52

3

而不是使用问号,你可以明确地使用可空关键字: 例如,

int?等于Nullable<int>

因此切换你的原创设计Nullable<T> ParseValue(string valueAsString)应该做的伎俩:只是做之后的通用实现。

2

如果您可以等待C#4.0,则可以使用dynamic关键字来解决此类情况。

0

这些列出的类型都有一个名为TryParse的静态方法。它们看起来很相似,但实际上签名是完全不同的。他们遵循类似的“模式”,但编译器无法检测到这种情况。

您可以尝试做这样的事情:

public T? ParseValue<T>(string value) where T : struct 
    { 
     if (typeof(T) == typeof(int)) 
     { 
      int i; 
      if (int.TryParse(value, out i)) 
       return (T)(object)i; 
      return null; 
     } 
     if (typeof(T) == typeof(decimal)) 
     { 
      decimal d; 
      if (decimal.TryParse(value, out d)) 
       return (T)(object)d; 
      return null; 
     } 
     // other supported types... 
     throw new ArgumentException("Type not supported"); 
    } 

然而,你不能。编译器无法知道(在编译时)如何将类型'T'转换为int(或任何其他类型)。

您可以重复投射以完成此项工作。 (感谢,Dotson)

用法:

 var mydecimal = ParseValue<decimal>("12.1"); 
     var myint = ParseValue<int>("-22"); 
     var badint = ParseValue<int>("Bad"); 
     // badint.HasValue == false 
+0

将我先投到对象上: (T)(object)i – 2009-12-10 06:01:33

0

其实,你可以更新什么马特的代码已经完成,并把它,这里是代码:

enter code here:static T? TryParse<T>(string parse) 
     where T : struct 
    { 
     Type t=typeof(T); 
     if (t==typeof(int)) 
     { 
      int i; 
      if (int.TryParse(parse, out i)) 
       return (T)(object)i; 
      return null; 
      //Console.WriteLine(t.Name); 
     } 
     if (t == typeof(double)) 
     { 
      double i; 
      if (double.TryParse(parse, out i)) 
       return (T)(object)i; 
      return null; 
     } 
     //blabla, more logic like datetime and other data types 
     return null; 
    } 

而且还可以像这样使用它: double? i = TryParse(“111.111”); int? a = TryParse(“111”);

3

最好实现一个扩展方法,你甚至可以解析枚举。这样,您就可以得到一个可空<ForAnyValueType>这样的:

public static T? Parse<T>(this string text) where T: struct 
    { 
     object o = null; 
     try { 
      var ttype = typeof(T); 
      if (ttype.IsEnum) 
      { 
       T n = default(T); 
       if (Enum.TryParse<T>(text, true, out n)) 
        return n; 
      } 
      else 
      o = Convert.ChangeType(text, ttype); 
     } 
     catch { } 

     if (o == null) 
      return new Nullable<T>(); 

     return new Nullable<T>((T)o); 
    } 
1

我真的不明白为什么使用安卓解决方案的元组,只要我们反正返回一个空的,好像在做同样的事情两次。我编辑他的解决方案使用TryParse并允许返回Nullable或一些默认值指定为参数。

/// <summary> 
    /// 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="aText"></param> 
    /// <returns></returns> 
    public static Nullable<T> TryParse<T>(this string aText) where T : struct { 
     T value; 
     if (ParsingBinder<T>.TryParse(aText, out value)) { 
      return value; 
     } 
     return null; 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="aText"></param> 
    /// <param name="aDefault"></param> 
    /// <returns></returns> 
    public static T TryParse<T>(this string aText, T aDefault) where T : struct { 
     T value; 
     if (!ParsingBinder<T>.TryParse(aText, out value)) { 
      value = aDefault; 
     } 
     return value; 
    } 

    /// <summary> 
    /// 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    static class ParsingBinder<T> where T : struct { 

     /// <summary> 
     /// 
     /// </summary> 
     /// <typeparam name="T"></typeparam> 
     /// <param name="aText"></param> 
     /// <param name="aOutput"></param> 
     /// <returns></returns> 
     public delegate bool Delegate_TryParse<T>(string aText, out T aOutput) where T : struct; 

     /// <summary> 
     /// 
     /// </summary> 
     static Delegate_TryParse<T> methodTryParse; 

     /// <summary> 
     /// 
     /// </summary> 
     /// <returns></returns> 
     public static Delegate_TryParse<T> TryParse { 
      get { 
       if (methodTryParse == null) { 
        methodTryParse = GetParserMethod(); 
       } 
       return methodTryParse; 
      } 
     } 

     /// <summary> 
     /// 
     /// </summary> 
     /// <returns></returns> 
     static Delegate_TryParse<T> GetParserMethod() { 
      var typeT = typeof(T); 
      var paramTypes = new Type[] { typeof(string), typeT.MakeByRefType() }; 
      var methodInfo = typeT.GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, null, paramTypes, null); 
      if (methodInfo == null) { 
       var message = String.Format("Unable to retrieve a 'TryParse' method for type '{0}'.", typeT.Name); 
       throw new Exception(message); 
      } 
      return (Delegate_TryParse<T>) Delegate.CreateDelegate(typeof(Delegate_TryParse<T>), methodInfo); 
     } 
    }