简体   繁体   English

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

[英]Convert string to nullable type (int, double, etc…)

I am attempting to do some data conversion. 我正在尝试进行一些数据转换。 Unfortunately, much of the data is in strings, where it should be int's or double, etc... 不幸的是,大部分数据都是字符串,它应该是int或double等等......

So what I've got is something like: 所以我得到的是:

double? amount = Convert.ToDouble(strAmount);

The problem with this approach is if strAmount is empty, if it's empty I want it to amount to be null, so when I add it into the database the column will be null. 这种方法的问题是如果strAmount为空,如果它为空,我希望它等于null,所以当我将它添加到数据库时,该列将为null。 So I ended up writing this: 所以我最后写了这个:

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

Now this works fine, but I now have five lines of code instead of one. 现在这个工作正常,但我现在有五行代码而不是一行代码。 This makes things a little more difficult to read, especially when I have a large amount of columns to convert. 这使得事情变得更难以阅读,特别是当我有大量的列要转换时。

I thought I'd use an extension to the string class and generic's to pass in the type, this is because it could be a double, or an int, or a long. 我以为我会使用字符串类和泛型的扩展来传入类型,这是因为它可能是double,或int或long。 So I tried this: 所以我尝试了这个:

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

But I get the error: Cannot convert type 'string' to 'T?' 但我收到错误:无法将类型'string'转换为'T?'

Is there a way around this? 有没有解决的办法? I am not very familiar with creating methods using generics. 我不太熟悉使用泛型创建方法。

Another thing to keep in mind is that the string itself might be null. 要记住的另一件事是字符串本身可能为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;
}

You could try using the below extension method: 您可以尝试使用以下扩展方法:

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

This way you can do this: 这样你可以这样做:

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

What about this: 那这个呢:


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

Of course, this doesn't take into account the convert failing. 当然,这并没有考虑转换失败。

I wrote this generic type converter. 我写了这个泛型转换器。 It works with Nullable and standard values, converting between all convertible types - not just string. 它适用于Nullable和标准值,在所有可转换类型之间进行转换 - 而不仅仅是字符串。 It handles all sorts of scenarios that you'd expect (default values, null values, other values, etc...) 它处理您期望的各种场景(默认值,空值,其他值等...)

I've been using this for about a year in dozens of production programs, so it should be pretty solid. 我已经在几十个生产程序中使用了大约一年,所以它应该非常可靠。

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

You might want to try: 您可能想尝试:

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

do your own null check and return int? 做你自己的null检查并返回int? if necessary. 如有必要。 You'll also want to wrap that in a try {} 你还想把它包装成try {}

Give this a shot... 试一试......

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

Then call it like this... 然后像这样称呼它......

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

You can use the following with objects, unfortunately this does not work with strings though. 您可以将以下内容与对象一起使用,但不幸的是,这不适用于字符串。

double? amount = (double?)someObject;

I use it for wrapping a session variable in a property (on a base page).. so my actual usage is (in my base page): 我用它来包装属性中的会话变量(在基页上)..所以我的实际用法是(在我的基页中):

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

I'm able to check for null in page logic: 我能够在页面逻辑中检查null:

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

I like Joel's answer, but I've modified it slightly as I'm not a fan of eating exceptions. 我喜欢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;
        }
    }

Here's something based on accepted answer. 这是基于公认答案的东西。 I removed the try/catch to make sure all the exceptions are not swallowed and not dealt with. 我删除了try / catch以确保不会吞下所有异常并且不处理。 Also made sure that the return variable (in accepted answer) is never initialized twice for nothing. 还要确保返回变量(在接受的答案中)永远不会被初始化两次。

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

There is no way around this. 没有办法解决这个问题。 Nullable, as well as your method, is constrained to using only value types as it's argument. Nullable以及您的方法仅限于使用值类型作为其参数。 String is a reference type and hence is incompatible with this declaration. String是引用类型,因此与此声明不兼容。

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

There is a generic solution (for any type). 有一个通用的解决方案(适用于任何类型)。 Usability is good, but implementation should be improved: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/ 可用性很好,但应该改进实施: http//cleansharp.de/wordpress/2011/05/generischer-typeconverter/

This allows you to write very clean code like this: 这允许你编写非常干净的代码,如下所示:

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

and also: 并且:

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

My example for anonimous types: 我的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>));

Another variation. 另一种变化。 This one 这个

  • Does not swallow exceptions 不吞下异常
  • Throws a NotSupportedException if the type can not be converted from string . 如果无法从string转换类型,则抛出NotSupportedException For instance, a custom struct without a type converter. 例如,没有类型转换器的自定义结构。
  • Otherwise returns a (T?)null if the string fails to parse. 否则,如果字符串无法解析,则返回(T?)null No need to check for null or whitespace. 无需检查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;
}

Let's add one more similar solution to the stack. 让我们在堆栈中添加一个类似的解决方案。 This one also parses enums, and it looks nice. 这个也解析枚举,看起来不错。 Very safe. 非常安全。

/// <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 https://github.com/Pangamma/PangammaUtilities-CSharp/blob/master/PangammaUtilities/Extensions/ToNullableStringExtension.cs

The generic answer provided by " Joel Coehoorn " is good. Joel Coehoorn ”提供的通用答案很好。

But, this is another way without using those GetConverter... or try/catch blocks... (i'm not sure but this may have better performance in some cases): 但是,这是另一种不使用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;
}

Usage is as following: 用法如下:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM