首页 文章

将字符串转换为可空类型(int,double等...)

提问于
浏览
127

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

所以我得到的是:

double? amount = Convert.ToDouble(strAmount);

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

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

现在这个工作正常,但我现在有五行代码而不是一行代码 . 这使得事情变得更难以阅读,特别是当我有大量的列要转换时 .

我以为我会使用字符串类和泛型的扩展来传入类型,这是因为它可能是double,或int或long . 所以我尝试了这个:

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;
        }
    }

但我收到错误:无法将类型'string'转换为'T?'

有没有解决的办法?我不太熟悉使用泛型创建方法 .

16 回答

  • 149
    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;
            }
        }
    }
    
  • 5

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

    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;
    }
    
  • 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>();
    
  • 2

    那这个呢:

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

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

  • 0

    我写了这个泛型转换器 . 它适用于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;
            }
        }
    
  • 22

    您可能想尝试:

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

    做你自己的空检查并在必要时返回 int? . 你还想把它包装在 try {}

  • 3

    试一试......

    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);
    }
    
  • 22

    我喜欢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;
            }
        }
    
  • 0

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

    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
    
  • 9

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

  • 3

    有一个通用的解决方案(适用于任何类型) . 可用性很好,但应该改进实施: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>();
    
  • 5

    这是基于公认答案的东西 . 我删除了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>);
    }
    
  • 3

    我的anonimous类型的例子:

    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>));
    
  • 48

    另一种变化 . 这个

    • 不吞下异常

    • 如果无法从 string 转换类型,则抛出 NotSupportedException . 例如,没有类型转换器的自定义结构 .

    • 如果字符串无法解析,则返回 (T?)null . 无需检查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;
    }
    
  • 3

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

    /// <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

  • 6

    “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
    

相关问题