【问题标题】:Convert string to nullable type (int, double, etc...)将字符串转换为可空类型(int、double 等...)
【发布时间】:2010-10-20 20:38:26
【问题描述】:

我正在尝试进行一些数据转换。不幸的是,大部分数据都在字符串中,它应该是 int 或 double 等......

所以我得到的是这样的:

double? amount = Convert.ToDouble(strAmount);

这种方法的问题是如果 strAmount 为空,如果它为空,我希望它为空,所以当我将它添加到数据库中时,该列将为空。所以我最终写了这个:

double? amount = null;
if(strAmount.Trim().Length>0)
{
    amount = Convert.ToDouble(strAmount);
}

现在这工作正常,但我现在有 5 行代码而不是 1 行。这使事情变得更难阅读,尤其是当我有大量的列要转换时。

我想我会使用对字符串类和泛型的扩展来传递类型,这是因为它可以是双精度、整数或长整数。所以我尝试了这个:

public static class GenericExtension
{
    public static Nullable<T> ConvertToNullable<T>(this string s, T type) where T: struct
    {
        if (s.Trim().Length > 0)
        {
            return (Nullable<T>)s;
        }
        return null;
    }
}

但我收到错误:无法将类型“字符串”转换为“T?”

有没有办法解决这个问题?我对使用泛型创建方法不是很熟悉。

【问题讨论】:

标签: c# .net generics extension-methods type-conversion


【解决方案1】:

要记住的另一件事是字符串本身可能为空。

public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
    Nullable<T> result = new Nullable<T>();
    try
    {
        if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0)
        {
            TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
            result = (T)conv.ConvertFrom(s);
        }
    }
    catch { } 
    return result;
}

【讨论】:

  • “T型”参数不用,可以省略。
  • +1,快点打败我吧。一个小问题:转换后的值需要直接分配给 result,而不是 result.Value。即,“结果 = (T)conv.ConvertFrom(s);”。
  • 如果你使用 .Net4,这可以用 string.IsNullOrWhiteSpace() 简化一下
  • @andrefadila - 使用:string sampleVendorId = "";诠释? vendorId = sampleVendorId.ToNullable();
  • 调用 conv.ConvertFrom 不会转换可空类型的 T,这使得这个函数有点反直觉。您不需要在此函数中使用 try catch。这三行代码说明了一切: if (string.IsNullOrWhiteSpace(stringObject)) return null; var conv = TypeDescriptor.GetConverter(typeof(T)); return (T?)conv.ConvertFrom(stringObject);
【解决方案2】:

您可以尝试使用以下扩展方法:

public static T? GetValueOrNull<T>(this string valueAsString)
    where T : struct 
{
    if (string.IsNullOrEmpty(valueAsString))
        return null;
    return (T) Convert.ChangeType(valueAsString, typeof(T));
}

这样你就可以做到:

double? amount = strAmount.GetValueOrNull<double>();
int? amount = strAmount.GetValueOrNull<int>();
decimal? amount = strAmount.GetValueOrNull<decimal>();

【讨论】:

  • 恕我直言,这是解决问题的最优雅的方法
  • 实际上.. 这个解决方案不起作用。 changetype 不会转换为可为空的类型。而是使用类型转换器
  • 这就是我需要知道的...在使用 Convert.ChangeType-Method 时,我必须使用 Nullable-Type 的底层类型。因为它不适用于参数转换类型的 Nullable-Typ。
【解决方案3】:

这个呢:


double? amount = string.IsNullOrEmpty(strAmount) ? (double?)null : Convert.ToDouble(strAmount);

当然,这并没有考虑转换失败。

【讨论】:

  • 如果您将任一返回值转换为双精度值? (或int?等),那么它将能够将它们转换为最终的double?。请参阅上面的更改。
  • 很抱歉。在编译器尖叫之前总是忘记演员表。 :)
  • 如果你不是 null 并且你尝试 amount.HasValue 并将 amount 声明为 var,这将失败。
【解决方案4】:

我编写了这个通用类型转换器。它适用于 Nullable 和标准值,在所有可转换类型之间进行转换 - 而不仅仅是字符串。它可以处理您期望的各种场景(默认值、空值、其他值等...)

我已经在几十个生产程序中使用了大约一年,所以它应该很可靠。

    public static T To<T>(this IConvertible obj)
    {
        Type t = typeof(T);

        if (t.IsGenericType
            && (t.GetGenericTypeDefinition() == typeof(Nullable<>)))
        {
            if (obj == null)
            {
                return (T)(object)null;
            }
            else
            {
                return (T)Convert.ChangeType(obj, Nullable.GetUnderlyingType(t));
            }
        }
        else
        {
            return (T)Convert.ChangeType(obj, t);
        }
    }

    public static T ToOrDefault<T>
                 (this IConvertible obj)
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return default(T);
        }
    }

    public static bool ToOrDefault<T>
                        (this IConvertible obj,
                         out T newObj)
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = default(T);
            return false;
        }
    }

    public static T ToOrOther<T>
                           (this IConvertible obj,
                           T other)
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return other;
        }
    }

    public static bool ToOrOther<T>
                             (this IConvertible obj,
                             out T newObj,
                             T other)
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = other;
            return false;
        }
    }

    public static T ToOrNull<T>
                          (this IConvertible obj)
                          where T : class
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return null;
        }
    }

    public static bool ToOrNull<T>
                      (this IConvertible obj,
                      out T newObj)
                      where T : class
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = null;
            return false;
        }
    }

【讨论】:

  • 我不认为忽略所有转换错误是正确的做法。此外,您可能不应该吞下各种异常。如果您无法将其缩小到一组固定的异常类型,请至少重新抛出 OutOfMemoryException
【解决方案5】:

你可能想试试:

TypeConverter conv = TypeDescriptor.GetConverter(typeof(int));
conv.ConvertFrom(mystring);

进行自己的空值检查并在必要时返回int?。您还需要将其包装在 try {}

【讨论】:

    【解决方案6】:

    试一试...

    public delegate bool TryParseDelegate<T>(string data, out T output);
    
    public static T? ToNullablePrimitive<T>(this string data, 
        TryParseDelegate<T> func) where T:struct
    {
        string.IsNullOrEmpty(data) return null;
    
        T output;
    
        if (func(data, out output))
        {
            return (T?)output;
        }
    
        return null;
    }
    

    那就这样称呼吧……

    void doStuff()
    {
        string foo = "1.0";
    
        double? myDouble = foo.ToNullablePrimitive<double>(double.TryParse);
    
        foo = "1";
    
        int? myInt = foo.ToNullablePrimitive<int>(int.TryParse);
    
        foo = "haha";
    
        int? myInt2 = foo.ToNullablePrimitive<int>(int.TryParse);
    }
    

    【讨论】:

      【解决方案7】:

      我喜欢 Joel 的回答,但我稍微修改了一下,因为我不喜欢吃例外。

          /// <summary>
          /// Converts a string to the specified nullable type.
          /// </summary>
          /// <typeparam name="T">The type to convert to</typeparam>
          /// <param name="s">The string to convert</param>
          /// <returns>The nullable output</returns>
          public static T? ToNullable<T>(this string s) where T : struct
          {
              if (string.IsNullOrWhiteSpace(s))
                  return null;
      
              TypeConverter conv = TypeDescriptor.GetConverter(typeof (T));
              return (T) conv.ConvertFrom(s);
          }
      
          /// <summary>
          /// Attempts to convert a string to the specified nullable primative.
          /// </summary>
          /// <typeparam name="T">The primitive type to convert to</typeparam>
          /// <param name="data">The string to convert</param>
          /// <param name="output">The nullable output</param>
          /// <returns>
          /// True if conversion is successfull, false otherwise.  Null and whitespace will
          /// be converted to null and return true.
          /// </returns>
          public static bool TryParseNullable<T>(this string data, out T? output) where T : struct
          {
              try
              {
                  output = data.ToNullable<T>();
                  return true;
              }
              catch
              {
                  output = null;
                  return false;
              }
          }
      

      【讨论】:

        【解决方案8】:

        您可以将以下内容与对象一起使用,但不幸的是,这不适用于字符串。

        double? amount = (double?)someObject;
        

        我用它来将会话变量包装在一个属性中(在一个基本页面上)。所以我的实际用法是(在我的基本页面上):

        public int? OrganisationID
        {
            get { return (int?)Session[Constants.Session_Key_OrganisationID]; }
            set { Session[Constants.Session_Key_OrganisationID] = value; }
        }
        

        我可以在页面逻辑中检查 null:

        if (base.OrganisationID == null)
            // do stuff
        

        【讨论】:

        • 嗨,谢谢,这为我解决了。仅供参考,我使用的是 VB.NET,而 VB 等价的 CType(Object, Nullable(Of Double)) 适用于字符串
        • 您的第一个示例是否有可以与字符串一起使用的版本?
        【解决方案9】:

        没有办法解决这个问题。 Nullable 以及您的方法都被限制为仅使用值类型作为其参数。 String 是一种引用类型,因此与此声明不兼容。

        【讨论】:

          【解决方案10】:
          public static class GenericExtension
          {
              public static T? ConvertToNullable<T>(this String s) where T : struct 
              {
                  try
                  {
                      return (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(s);
                  }
                  catch (Exception)
                  {
                      return null;
                  }
              }
          }
          

          【讨论】:

            【解决方案11】:

            有一个通用解决方案(适用于任何类型)。可用性很好,但应该改进实现: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

            这使您可以编写非常干净的代码,如下所示:

            string value = null;
            int? x = value.ConvertOrDefault<int?>();
            

            还有:

            object obj = 1;  
            
            string value = null;
            int x = 5;
            if (value.TryConvert(out x))
                Console.WriteLine("TryConvert example: " + x); 
            
            bool boolean = "false".ConvertOrDefault<bool>();
            bool? nullableBoolean = "".ConvertOrDefault<bool?>();
            int integer = obj.ConvertOrDefault<int>();
            int negativeInteger = "-12123".ConvertOrDefault<int>();
            int? nullableInteger = value.ConvertOrDefault<int?>();
            MyEnum enumValue = "SecondValue".ConvertOrDefault<MyEnum>();
            
            MyObjectBase myObject = new MyObjectClassA();
            MyObjectClassA myObjectClassA = myObject.ConvertOrDefault<MyObjectClassA>();
            

            【讨论】:

            • 谁投了反对票,请添加评论这个通用解决方案有什么问题。
            • 嗯,首先你的答案有问题,那就是“你可以忘记所有其他答案”。即使这是真的(事实并非如此),这也是错误的。而“通用解决方案”的问题在于它充满了糟糕的性能(typeName.IndexOf?真的吗?)和奇怪的行为(显示的TryConvert 函数甚至不能正确处理空值)。
            【解决方案12】:

            这是基于已接受答案的内容。我删除了 try/catch 以确保所有异常都不会被吞下和处理。还要确保返回变量(在接受的答案中)永远不会被初始化两次。

            public static Nullable<T> ToNullable<T>(this string s) where T: struct
            {
                if (!string.IsNullOrWhiteSpace(s))
                {
                    TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
            
                    return (T)conv.ConvertFrom(s);
                }
            
                return default(Nullable<T>);
            }
            

            【讨论】:

              【解决方案13】:

              我的匿名类型示例:

              private object ConvertNullable(object value, Type nullableType)
              {
                  Type resultType = typeof(Nullable<>).MakeGenericType(nullableType.GetGenericArguments());
                  return Activator.CreateInstance(resultType, Convert.ChangeType(value, nullableType.GetGenericArguments()[0]));
              }
              
              ...
              
              Type anonimousType = typeof(Nullable<int>);
              object nullableInt1 = ConvertNullable("5", anonimousType);
              // or evident Type
              Nullable<int> nullableInt2 = (Nullable<int>)ConvertNullable("5", typeof(Nullable<int>));
              

              【讨论】:

                【解决方案14】:

                另一种变化。这个

                • 不吞并异常
                • 如果无法从string 转换类型,则抛出NotSupportedException。例如,没有类型转换器的自定义结构。
                • 否则,如果字符串解析失败,则返回(T?)null。无需检查空值或空格。
                using System.ComponentModel;
                
                public static Nullable<T> ToNullable<T>(this string s) where T : struct
                {
                    var ret = new Nullable<T>();
                    var conv = TypeDescriptor.GetConverter(typeof(T));
                
                    if (!conv.CanConvertFrom(typeof(string)))
                    {
                        throw new NotSupportedException();
                    }
                
                    if (conv.IsValid(s))
                    {
                        ret = (T)conv.ConvertFrom(s);
                    }
                
                    return ret;
                }
                

                【讨论】:

                  【解决方案15】:

                  让我们在堆栈中再添加一个类似的解决方案。这个也解析枚举,看起来不错。非常安全。

                  /// <summary>
                      /// <para>More convenient than using T.TryParse(string, out T). 
                      /// Works with primitive types, structs, and enums.
                      /// Tries to parse the string to an instance of the type specified.
                      /// If the input cannot be parsed, null will be returned.
                      /// </para>
                      /// <para>
                      /// If the value of the caller is null, null will be returned.
                      /// So if you have "string s = null;" and then you try "s.ToNullable...",
                      /// null will be returned. No null exception will be thrown. 
                      /// </para>
                      /// <author>Contributed by Taylor Love (Pangamma)</author>
                      /// </summary>
                      /// <typeparam name="T"></typeparam>
                      /// <param name="p_self"></param>
                      /// <returns></returns>
                      public static T? ToNullable<T>(this string p_self) where T : struct
                      {
                          if (!string.IsNullOrEmpty(p_self))
                          {
                              var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
                              if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self);
                              if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;}
                          }
                  
                          return null;
                      }
                  

                  https://github.com/Pangamma/PangammaUtilities-CSharp/blob/master/PangammaUtilities/Extensions/ToNullableStringExtension.cs

                  【讨论】:

                    【解决方案16】:

                    Joel Coehoorn”提供的通用答案很好。

                    但是,这是不使用 GetConverter...try/catch 块的另一种方法...(我不确定,但在某些情况下这可能具有更好的性能):

                    public static class StrToNumberExtensions
                    {
                        public static short ToShort(this string s, short defaultValue = 0) => short.TryParse(s, out var v) ? v : defaultValue;
                        public static int ToInt(this string s, int defaultValue = 0) => int.TryParse(s, out var v) ? v : defaultValue;
                        public static long ToLong(this string s, long defaultValue = 0) => long.TryParse(s, out var v) ? v : defaultValue;
                        public static decimal ToDecimal(this string s, decimal defaultValue = 0) => decimal.TryParse(s, out var v) ? v : defaultValue;
                        public static float ToFloat(this string s, float defaultValue = 0) => float.TryParse(s, out var v) ? v : defaultValue;
                        public static double ToDouble(this string s, double defaultValue = 0) => double.TryParse(s, out var v) ? v : defaultValue;
                    
                        public static short? ToshortNullable(this string s, short? defaultValue = null) => short.TryParse(s, out var v) ? v : defaultValue;
                        public static int? ToIntNullable(this string s, int? defaultValue = null) => int.TryParse(s, out var v) ? v : defaultValue;
                        public static long? ToLongNullable(this string s, long? defaultValue = null) => long.TryParse(s, out var v) ? v : defaultValue;
                        public static decimal? ToDecimalNullable(this string s, decimal? defaultValue = null) => decimal.TryParse(s, out var v) ? v : defaultValue;
                        public static float? ToFloatNullable(this string s, float? defaultValue = null) => float.TryParse(s, out var v) ? v : defaultValue;
                        public static double? ToDoubleNullable(this string s, double? defaultValue = null) => double.TryParse(s, out var v) ? v : defaultValue;
                    }
                    

                    用法如下:

                    var x1 = "123".ToInt(); //123
                    var x2 = "abc".ToInt(); //0
                    var x3 = "abc".ToIntNullable(); // (int?)null 
                    int x4 = ((string)null).ToInt(-1); // -1
                    int x5 = "abc".ToInt(-1); // -1
                    
                    var y = "19.50".ToDecimal(); //19.50
                    
                    var z1 = "invalid number string".ToDoubleNullable(); // (double?)null
                    var z2 = "invalid number string".ToDoubleNullable(0); // (double?)0
                    

                    【讨论】:

                    • @MassimilianoKraus 可能是,但它是一个简单的 12 行代码,编写一次,但一直使用。而且,正如我所说,它应该/可能比使用那些 TypeDescriptor.GetConverter... 代码更快。这只是另一种方式。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-05-25
                    相关资源
                    最近更新 更多