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


137

我正在尝试进行一些数据转换。不幸的是,许多数据都是字符串形式,应该是int或double等等。

所以我得到的是这样的:

double? amount = Convert.ToDouble(strAmount);

这种方法的问题是,如果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?'

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


Answers:


157

要记住的另一件事是字符串本身可能为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
您可以省略“ T type”参数,因为它没有使用。
迈克尔·梅多斯

1
+1,就打败我吧。一个小巧的选择:转换后的值需要直接分配给result,而不是result.Value。即“结果=(T)conv.ConvertFrom(s);”。
2009年

20
如果您使用.Net4,可以使用string.IsNullOrWhiteSpace()进行一些简化
Sergej

1
@andrefadila-使用:字符串sampleVendorId =“”; 诠释?vendorId = sampleVendorId.ToNullable <int>();
minerva

1
调用conv.ConvertFrom不会转换T的可为空的类型,这使该函数有点反感。您不需要尝试使用此功能。这三行代码使一切成为可能:if(string.IsNullOrWhiteSpace(stringObject))返回null;var conv = TypeDescriptor.GetConverter(typeof(T)); 返回(T?)conv.ConvertFrom(stringObject);
大卫,

54

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

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>();

3
恕我直言,这是解决问题的最优雅的方法
Zaffiro

4
实际上..此解决方案不起作用。changetype不会转换为可为null的类型。改用typeconverter
AaronHS

这就是我需要知道的...使用Convert.ChangeType-Method时,我必须使用Nullable-Type的基础类型。因为它不适用于参数conversionType的Nullable-Typ。
Marcus.D

27

那这个呢:


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

当然,这没有考虑转换失败的问题。


如果将任一返回值都转换为双精度值?(或int?等),则可以将它们转换为最终的double?。请参阅上面的更改。
bdukes

对于那个很抱歉。在编译器尖叫之前,请始终不要进行强制转换。:)
约翰·卡夫

如果您不为null并尝试使用amount.HasValue并将其声明为var,则此操作将失败。
史蒂夫

23

我写了这个通用类型转换器。它与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;
        }
    }

2
我认为忽略所有转换错误不是正确的选择。同样,您可能不应该吞下各种异常。OutOfMemoryException如果无法将其范围缩小到一组固定的异常类型,则至少重新抛出。
Paul Groke 2013年

9

您可能要尝试:

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

6

我喜欢乔尔(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;
        }
    }

5

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

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))
等值

您的第一个示例是否有可以与字符串一起使用的版本?
wazz

3

这是没有办法的。可空值以及您的方法被限制为仅使用值类型作为参数。字符串是引用类型,因此与此声明不兼容。


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

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>();

谁在投票,请添加注释此通用解决方案出了什么问题。
帕维尔·霍德克

1
好吧,首先,您的答案有一个非常错误的地方,那就是“您可以忘记所有其他答案”。即使是正确的,这也是错误的(不是)。“通用解决方案”的问题在于它充满了糟糕的性能(typeName.IndexOf真的吗?)和扼杀行为(所示TryConvert函数甚至无法正确处理空值)。
Paul Groke

3

这是基于公认答案的内容。我删除了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>);
}

2

我的非凡类型示例:

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

2

另一个变化。这个

  • 不吞咽异常
  • NotSupportedException如果类型不能从转换,则抛出a string。例如,没有类型转换器的自定义结构。
  • 否则,(T?)null如果字符串解析失败,则返回a 。无需检查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;
}

1

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

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


0

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代码更快。这只是另一种方式。
S.Serpooshan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.